1use 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#[derive(Debug, Clone)]
18pub struct QCAConfig {
19 pub dimensions: Vec<usize>,
21 pub boundary_conditions: BoundaryConditions,
23 pub neighborhood: NeighborhoodType,
25 pub rule_type: QCARuleType,
27 pub evolution_steps: usize,
29 pub parallel_evolution: bool,
31 pub measurement_strategy: MeasurementStrategy,
33}
34
35impl Default for QCAConfig {
36 fn default() -> Self {
37 Self {
38 dimensions: vec![16], 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub enum BoundaryConditions {
52 Periodic,
54 Fixed,
56 Open,
58 Reflective,
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub enum NeighborhoodType {
65 VonNeumann,
67 Moore,
69 Custom,
71 Extended,
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub enum QCARuleType {
78 Partitioned,
80 Global,
82 Sequential,
84 Margolus,
86 Custom,
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92pub enum MeasurementStrategy {
93 Probabilistic,
95 Deterministic,
97 Partial,
99 Continuous,
101}
102
103pub struct QuantumCellularAutomaton {
105 config: QCAConfig,
107 state: Array1<Complex64>,
109 evolution_rules: Vec<QCARule>,
111 cell_mapping: CellMapping,
113 backend: Option<SciRS2Backend>,
115 evolution_history: Vec<QCASnapshot>,
117 stats: QCAStats,
119}
120
121#[derive(Debug, Clone)]
123pub struct QCARule {
124 pub id: String,
126 pub unitary: Array2<Complex64>,
128 pub affected_cells: Vec<usize>,
130 pub neighborhood_pattern: Vec<(i32, i32, i32)>, pub parameters: HashMap<String, f64>,
134}
135
136#[derive(Debug, Clone)]
138pub struct CellMapping {
139 pub total_cells: usize,
141 pub dimensions: Vec<usize>,
143 pub coord_to_index: HashMap<Vec<usize>, usize>,
145 pub index_to_coord: HashMap<usize, Vec<usize>>,
147 pub neighbors: HashMap<usize, Vec<usize>>,
149}
150
151#[derive(Debug, Clone)]
153pub struct QCASnapshot {
154 pub time_step: usize,
156 pub state: Array1<Complex64>,
158 pub measurements: Option<Vec<f64>>,
160 pub entanglement_entropy: Option<f64>,
162 pub local_observables: HashMap<String, f64>,
164}
165
166#[derive(Debug, Clone, Default, Serialize, Deserialize)]
168pub struct QCAStats {
169 pub evolution_steps: usize,
171 pub total_evolution_time_ms: f64,
173 pub avg_step_time_ms: f64,
175 pub measurements_performed: usize,
177 pub entropy_stats: EntropyStats,
179 pub rule_applications: HashMap<String, usize>,
181}
182
183#[derive(Debug, Clone, Default, Serialize, Deserialize)]
185pub struct EntropyStats {
186 pub max_entropy: f64,
188 pub min_entropy: f64,
190 pub avg_entropy: f64,
192 pub entropy_variance: f64,
194}
195
196impl QuantumCellularAutomaton {
197 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 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 pub fn with_backend(mut self) -> Result<Self> {
222 self.backend = Some(SciRS2Backend::new());
223 Ok(self)
224 }
225
226 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 pub fn add_rule(&mut self, rule: QCARule) -> Result<()> {
245 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 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 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 if step % 10 == 0 || step == steps - 1 {
274 let snapshot = self.take_snapshot(step)?;
275 self.evolution_history.push(snapshot);
276 }
277
278 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 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 fn evolve_1d_partitioned(&mut self) -> Result<()> {
318 let lattice_size = self.config.dimensions[0];
319
320 let mut partitions = Vec::new();
322 let partition_size = 2; 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 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 fn evolve_2d_partitioned(&mut self) -> Result<()> {
359 let (width, height) = (self.config.dimensions[0], self.config.dimensions[1]);
360
361 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 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 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 fn evolve_global(&mut self) -> Result<()> {
407 if let Some(global_rule) = self.evolution_rules.first() {
408 let new_state = global_rule.unitary.dot(&self.state);
410 self.state = new_state;
411
412 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 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 fn evolve_margolus(&mut self) -> Result<()> {
439 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; for y in (offset..height - 1).step_by(2) {
450 for x in (offset..width - 1).step_by(2) {
451 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 fn evolve_custom(&mut self) -> Result<()> {
468 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 *self
480 .stats
481 .rule_applications
482 .entry(rule.id.clone())
483 .or_insert(0) += 1;
484 }
485
486 Ok(())
487 }
488
489 fn apply_two_cell_rule(&mut self, cell1: usize, cell2: usize) -> Result<()> {
491 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 self.apply_two_qubit_unitary(cell1, cell2, &rule_data.0)?;
503
504 *self.stats.rule_applications.entry(rule_data.1).or_insert(0) += 1;
506
507 Ok(())
508 }
509
510 fn apply_neighborhood_rule(&mut self, center_cell: usize, neighbors: &[usize]) -> Result<()> {
512 for &neighbor in neighbors {
514 self.apply_two_cell_rule(center_cell, neighbor)?;
515 }
516 Ok(())
517 }
518
519 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 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 self.apply_four_qubit_unitary(cells, &rule_unitary)?;
538
539 Ok(())
540 }
541
542 fn apply_single_cell_rule(&mut self, cell: usize, unitary: &Array2<Complex64>) -> Result<()> {
544 self.apply_single_qubit_unitary(cell, unitary)
545 }
546
547 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 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 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 let masks: Vec<usize> = qubits.iter().map(|&q| 1 << q).collect();
640
641 for i in 0..state_size {
642 let base = i & !(masks[0] | masks[1] | masks[2] | masks[3]);
644
645 if i == base {
647 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 for out_state in 0..16 {
660 let mut new_amplitude = Complex64::new(0.0, 0.0);
661 for (in_state, &litude) 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 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)], };
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 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 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 fn apply_weak_measurements(&mut self) -> Result<()> {
821 let measurement_strength = 0.01; 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 let measurement_operator =
830 self.create_weak_measurement_operator(cell, measurement_strength);
831 self.state = measurement_operator.dot(&self.state);
832
833 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 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 let factor = Complex64::new((1.0 - strength).sqrt(), 0.0);
854 operator[[i, i]] = factor;
855 }
856 }
857
858 operator
859 }
860
861 fn take_snapshot(&mut self, time_step: usize) -> Result<QCASnapshot> {
863 let entropy = self.calculate_entanglement_entropy()?;
864
865 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 fn calculate_entanglement_entropy(&self) -> Result<f64> {
895 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 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 fn compute_eigenvalues(&self, matrix: &Array2<f64>) -> Result<Vec<f64>> {
943 let mut eigenvalues = Vec::new();
945
946 if matrix.dim().0 <= 4 {
948 for i in 0..matrix.dim().0 {
950 eigenvalues.push(matrix[[i, i]]); }
952 } else {
953 for i in 0..matrix.dim().0 {
955 eigenvalues.push(matrix[[i, i]]);
956 }
957 }
958
959 Ok(eigenvalues)
960 }
961
962 fn calculate_local_observables(&self) -> Result<HashMap<String, f64>> {
964 let mut observables = HashMap::new();
965
966 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 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 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 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, &litude) 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 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, &litude) 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 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 self.collapse_state_after_measurement(cell, result)?;
1029 }
1030
1031 self.stats.measurements_performed += cells.len();
1032 Ok(results)
1033 }
1034
1035 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, &litude) 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 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 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 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 pub const fn get_state(&self) -> &Array1<Complex64> {
1075 &self.state
1076 }
1077
1078 pub fn get_evolution_history(&self) -> &[QCASnapshot] {
1080 &self.evolution_history
1081 }
1082
1083 pub const fn get_stats(&self) -> &QCAStats {
1085 &self.stats
1086 }
1087
1088 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 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.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 let state_size = 1 << cell_mapping.total_cells.min(10); 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 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 let mut unitary = Array2::zeros((16, 16));
1254
1255 for i in 0..16 {
1257 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 let mut matrix = Array2::zeros((size, size));
1273
1274 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 for j in 0..size {
1283 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 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 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 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#[derive(Debug, Clone)]
1353pub struct QCAEvolutionResult {
1354 pub final_state: Array1<Complex64>,
1356 pub evolution_history: Vec<QCASnapshot>,
1358 pub total_steps: usize,
1360 pub total_time_ms: f64,
1362 pub final_entropy: f64,
1364}
1365
1366pub struct QCAUtils;
1368
1369impl QCAUtils {
1370 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 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 for i in 0..state_size {
1414 state[i] = Complex64::new(fastrand::f64() - 0.5, fastrand::f64() - 0.5);
1415 }
1416 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 let width = dimensions[0];
1423 let height = dimensions[1];
1424
1425 if width >= 3 && height >= 3 {
1426 let glider_positions = [
1428 width,
1429 2 * width + 1,
1430 2, 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 let amplitude = 1.0 / (state_size as f64).sqrt();
1450 state.fill(Complex64::new(amplitude, 0.0));
1451 }
1452 _ => {
1453 state[0] = Complex64::new(1.0, 0.0);
1455 }
1456 }
1457
1458 state
1459 }
1460
1461 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; 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#[derive(Debug, Clone, Default)]
1503pub struct QCABenchmarkResults {
1504 pub benchmark_times: Vec<(String, f64)>,
1506 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 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); 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 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); qca.state[15] = Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0); 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 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}