1use crate::error::{QuantRS2Error, QuantRS2Result};
9use ndarray::{Array1, Array2, Array3};
10use num_complex::Complex64;
11
12#[derive(Debug, Clone, Copy, PartialEq)]
14pub enum QCAType {
15 Partitioned,
17 Margolus,
19 Moore,
21 VonNeumann,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq)]
27pub enum BoundaryCondition {
28 Periodic,
30 Fixed,
32 Open,
34}
35
36pub trait QCARule {
38 fn apply(&self, neighborhood: &[Complex64]) -> QuantRS2Result<Vec<Complex64>>;
40
41 fn neighborhood_size(&self) -> usize;
43
44 fn is_reversible(&self) -> bool;
46}
47
48#[derive(Debug, Clone)]
50pub struct UnitaryRule {
51 pub unitary: Array2<Complex64>,
53 pub num_qubits: usize,
55}
56
57impl UnitaryRule {
58 pub fn new(unitary: Array2<Complex64>) -> QuantRS2Result<Self> {
60 let (rows, cols) = unitary.dim();
61 if rows != cols {
62 return Err(QuantRS2Error::InvalidInput(
63 "Unitary matrix must be square".to_string(),
64 ));
65 }
66
67 let conjugate_transpose = unitary.t().mapv(|x| x.conj());
69 let product = conjugate_transpose.dot(&unitary);
70
71 for i in 0..rows {
72 for j in 0..cols {
73 let expected = if i == j {
74 Complex64::new(1.0, 0.0)
75 } else {
76 Complex64::new(0.0, 0.0)
77 };
78 if (product[[i, j]] - expected).norm() > 1e-10 {
79 return Err(QuantRS2Error::InvalidInput(
80 "Matrix is not unitary".to_string(),
81 ));
82 }
83 }
84 }
85
86 let num_qubits = (rows as f64).log2() as usize;
87 if 1 << num_qubits != rows {
88 return Err(QuantRS2Error::InvalidInput(
89 "Matrix dimension must be a power of 2".to_string(),
90 ));
91 }
92
93 Ok(Self {
94 unitary,
95 num_qubits,
96 })
97 }
98
99 pub fn cnot() -> Self {
101 let unitary = Array2::from_shape_vec(
102 (4, 4),
103 vec![
104 Complex64::new(1.0, 0.0),
105 Complex64::new(0.0, 0.0),
106 Complex64::new(0.0, 0.0),
107 Complex64::new(0.0, 0.0),
108 Complex64::new(0.0, 0.0),
109 Complex64::new(1.0, 0.0),
110 Complex64::new(0.0, 0.0),
111 Complex64::new(0.0, 0.0),
112 Complex64::new(0.0, 0.0),
113 Complex64::new(0.0, 0.0),
114 Complex64::new(0.0, 0.0),
115 Complex64::new(1.0, 0.0),
116 Complex64::new(0.0, 0.0),
117 Complex64::new(0.0, 0.0),
118 Complex64::new(1.0, 0.0),
119 Complex64::new(0.0, 0.0),
120 ],
121 )
122 .unwrap();
123
124 Self::new(unitary).unwrap()
125 }
126
127 pub fn hadamard() -> Self {
129 let sqrt2_inv = 1.0 / 2.0_f64.sqrt();
130 let unitary = Array2::from_shape_vec(
131 (2, 2),
132 vec![
133 Complex64::new(sqrt2_inv, 0.0),
134 Complex64::new(sqrt2_inv, 0.0),
135 Complex64::new(sqrt2_inv, 0.0),
136 Complex64::new(-sqrt2_inv, 0.0),
137 ],
138 )
139 .unwrap();
140
141 Self::new(unitary).unwrap()
142 }
143
144 pub fn toffoli() -> Self {
146 let mut unitary = Array2::zeros((8, 8));
147
148 for i in 0..6 {
150 unitary[[i, i]] = Complex64::new(1.0, 0.0);
151 }
152
153 unitary[[6, 7]] = Complex64::new(1.0, 0.0);
155 unitary[[7, 6]] = Complex64::new(1.0, 0.0);
156
157 Self::new(unitary).unwrap()
158 }
159}
160
161impl QCARule for UnitaryRule {
162 fn apply(&self, neighborhood: &[Complex64]) -> QuantRS2Result<Vec<Complex64>> {
163 if neighborhood.len() != 1 << self.num_qubits {
164 return Err(QuantRS2Error::InvalidInput(
165 "Neighborhood size doesn't match rule dimension".to_string(),
166 ));
167 }
168
169 let state = Array1::from_vec(neighborhood.to_vec());
170 let evolved = self.unitary.dot(&state);
171 Ok(evolved.to_vec())
172 }
173
174 fn neighborhood_size(&self) -> usize {
175 1 << self.num_qubits
176 }
177
178 fn is_reversible(&self) -> bool {
179 true }
181}
182
183pub struct QuantumCellularAutomaton1D {
185 pub num_sites: usize,
187 pub state: Array2<Complex64>,
189 rule: Box<dyn QCARule + Send + Sync>,
191 boundary: BoundaryCondition,
193 qca_type: QCAType,
195 pub time_step: usize,
197}
198
199impl QuantumCellularAutomaton1D {
200 pub fn new(
202 num_sites: usize,
203 rule: Box<dyn QCARule + Send + Sync>,
204 boundary: BoundaryCondition,
205 qca_type: QCAType,
206 ) -> Self {
207 let mut state = Array2::zeros((num_sites, 2));
209 for i in 0..num_sites {
210 state[[i, 0]] = Complex64::new(1.0, 0.0); }
212
213 Self {
214 num_sites,
215 state,
216 rule,
217 boundary,
218 qca_type,
219 time_step: 0,
220 }
221 }
222
223 pub fn set_site_state(
225 &mut self,
226 site: usize,
227 amplitudes: [Complex64; 2],
228 ) -> QuantRS2Result<()> {
229 if site >= self.num_sites {
230 return Err(QuantRS2Error::InvalidInput(
231 "Site index out of bounds".to_string(),
232 ));
233 }
234
235 let norm = (amplitudes[0].norm_sqr() + amplitudes[1].norm_sqr()).sqrt();
237 if norm < 1e-10 {
238 return Err(QuantRS2Error::InvalidInput(
239 "State cannot have zero norm".to_string(),
240 ));
241 }
242
243 self.state[[site, 0]] = amplitudes[0] / norm;
244 self.state[[site, 1]] = amplitudes[1] / norm;
245
246 Ok(())
247 }
248
249 pub fn get_site_state(&self, site: usize) -> QuantRS2Result<[Complex64; 2]> {
251 if site >= self.num_sites {
252 return Err(QuantRS2Error::InvalidInput(
253 "Site index out of bounds".to_string(),
254 ));
255 }
256
257 Ok([self.state[[site, 0]], self.state[[site, 1]]])
258 }
259
260 pub fn initialize_random(&mut self, rng: &mut dyn rand::RngCore) -> QuantRS2Result<()> {
262 use rand::Rng;
263
264 for i in 0..self.num_sites {
265 let theta = rng.random_range(0.0..std::f64::consts::PI);
266 let phi = rng.random_range(0.0..2.0 * std::f64::consts::PI);
267
268 let amp0 = Complex64::new(theta.cos(), 0.0);
269 let amp1 = Complex64::new(theta.sin() * phi.cos(), theta.sin() * phi.sin());
270
271 self.set_site_state(i, [amp0, amp1])?;
272 }
273
274 Ok(())
275 }
276
277 pub fn step(&mut self) -> QuantRS2Result<()> {
279 match self.qca_type {
280 QCAType::Partitioned => self.step_partitioned(),
281 QCAType::Margolus => self.step_margolus(),
282 QCAType::Moore => self.step_moore(),
283 QCAType::VonNeumann => self.step_von_neumann(),
284 }
285 }
286
287 fn step_partitioned(&mut self) -> QuantRS2Result<()> {
289 let rule_size = self.rule.neighborhood_size();
290 let num_qubits = (rule_size as f64).log2() as usize;
291
292 if num_qubits != 2 {
293 return Err(QuantRS2Error::InvalidInput(
294 "Partitioned QCA currently supports only 2-qubit rules".to_string(),
295 ));
296 }
297
298 let mut new_state = self.state.clone();
299 let offset = self.time_step % 2;
300
301 for i in (offset..self.num_sites.saturating_sub(1)).step_by(2) {
303 let j = (i + 1) % self.num_sites;
304
305 let neighborhood = self.get_pair_state_vector(i, j)?;
307
308 let evolved = self.rule.apply(&neighborhood)?;
310
311 new_state[[i, 0]] = evolved[0];
313 new_state[[i, 1]] = evolved[1];
314 new_state[[j, 0]] = evolved[2];
315 new_state[[j, 1]] = evolved[3];
316 }
317
318 self.state = new_state;
319 self.time_step += 1;
320 Ok(())
321 }
322
323 fn get_pair_state_vector(&self, site1: usize, site2: usize) -> QuantRS2Result<Vec<Complex64>> {
325 let amp1_0 = self.state[[site1, 0]];
326 let amp1_1 = self.state[[site1, 1]];
327 let amp2_0 = self.state[[site2, 0]];
328 let amp2_1 = self.state[[site2, 1]];
329
330 Ok(vec![
332 amp1_0 * amp2_0, amp1_0 * amp2_1, amp1_1 * amp2_0, amp1_1 * amp2_1, ])
337 }
338
339 fn step_margolus(&mut self) -> QuantRS2Result<()> {
341 self.step_partitioned()
343 }
344
345 fn step_moore(&mut self) -> QuantRS2Result<()> {
347 let rule_size = self.rule.neighborhood_size();
348 if rule_size != 8 {
349 return Err(QuantRS2Error::InvalidInput(
351 "Moore neighborhood requires 3-qubit rule".to_string(),
352 ));
353 }
354
355 let mut new_state = self.state.clone();
356
357 for i in 0..self.num_sites {
358 let left = self.get_neighbor(i, -1);
359 let center = i;
360 let right = self.get_neighbor(i, 1);
361
362 let neighborhood = self.get_triple_state_vector(left, center, right)?;
364
365 let evolved = self.rule.apply(&neighborhood)?;
367
368 new_state[[center, 0]] = evolved[2]; new_state[[center, 1]] = evolved[6];
371 }
372
373 self.state = new_state;
374 self.time_step += 1;
375 Ok(())
376 }
377
378 fn step_von_neumann(&mut self) -> QuantRS2Result<()> {
380 self.step_moore()
382 }
383
384 fn get_neighbor(&self, site: usize, offset: isize) -> usize {
386 match self.boundary {
387 BoundaryCondition::Periodic => {
388 let new_site = site as isize + offset;
389 ((new_site % self.num_sites as isize + self.num_sites as isize)
390 % self.num_sites as isize) as usize
391 }
392 BoundaryCondition::Fixed | BoundaryCondition::Open => {
393 let new_site = site as isize + offset;
394 if new_site < 0 {
395 0
396 } else if new_site >= self.num_sites as isize {
397 self.num_sites - 1
398 } else {
399 new_site as usize
400 }
401 }
402 }
403 }
404
405 fn get_triple_state_vector(
407 &self,
408 site1: usize,
409 site2: usize,
410 site3: usize,
411 ) -> QuantRS2Result<Vec<Complex64>> {
412 let mut state_vector = vec![Complex64::new(0.0, 0.0); 8];
413
414 for i in 0..2 {
416 for j in 0..2 {
417 for k in 0..2 {
418 let amp =
419 self.state[[site1, i]] * self.state[[site2, j]] * self.state[[site3, k]];
420 let idx = 4 * i + 2 * j + k;
421 state_vector[idx] = amp;
422 }
423 }
424 }
425
426 Ok(state_vector)
427 }
428
429 pub fn entanglement_entropy(
431 &self,
432 region_start: usize,
433 region_size: usize,
434 ) -> QuantRS2Result<f64> {
435 if region_start + region_size > self.num_sites {
436 return Err(QuantRS2Error::InvalidInput(
437 "Region extends beyond lattice".to_string(),
438 ));
439 }
440
441 let mut entropy = 0.0;
444
445 for i in region_start..region_start + region_size {
446 let p0 = self.state[[i, 0]].norm_sqr();
447 let p1 = self.state[[i, 1]].norm_sqr();
448
449 if p0 > 1e-12 {
450 entropy -= p0 * p0.ln();
451 }
452 if p1 > 1e-12 {
453 entropy -= p1 * p1.ln();
454 }
455 }
456
457 Ok(entropy)
458 }
459
460 pub fn site_probabilities(&self) -> Vec<[f64; 2]> {
462 self.state
463 .rows()
464 .into_iter()
465 .map(|row| [row[0].norm_sqr(), row[1].norm_sqr()])
466 .collect()
467 }
468
469 pub fn correlation(&self, site1: usize, site2: usize) -> QuantRS2Result<Complex64> {
471 if site1 >= self.num_sites || site2 >= self.num_sites {
472 return Err(QuantRS2Error::InvalidInput(
473 "Site index out of bounds".to_string(),
474 ));
475 }
476
477 let z1 = self.state[[site1, 0]].norm_sqr() - self.state[[site1, 1]].norm_sqr();
479 let z2 = self.state[[site2, 0]].norm_sqr() - self.state[[site2, 1]].norm_sqr();
480
481 Ok(Complex64::new(z1 * z2, 0.0))
482 }
483}
484
485impl std::fmt::Debug for QuantumCellularAutomaton1D {
486 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
487 f.debug_struct("QuantumCellularAutomaton1D")
488 .field("num_sites", &self.num_sites)
489 .field("boundary", &self.boundary)
490 .field("qca_type", &self.qca_type)
491 .field("time_step", &self.time_step)
492 .finish()
493 }
494}
495
496pub struct QuantumCellularAutomaton2D {
498 pub width: usize,
500 pub height: usize,
502 pub state: Array3<Complex64>,
504 rule: Box<dyn QCARule + Send + Sync>,
506 boundary: BoundaryCondition,
508 qca_type: QCAType,
510 pub time_step: usize,
512}
513
514impl QuantumCellularAutomaton2D {
515 pub fn new(
517 width: usize,
518 height: usize,
519 rule: Box<dyn QCARule + Send + Sync>,
520 boundary: BoundaryCondition,
521 qca_type: QCAType,
522 ) -> Self {
523 let mut state = Array3::zeros((width, height, 2));
525 for i in 0..width {
526 for j in 0..height {
527 state[[i, j, 0]] = Complex64::new(1.0, 0.0); }
529 }
530
531 Self {
532 width,
533 height,
534 state,
535 rule,
536 boundary,
537 qca_type,
538 time_step: 0,
539 }
540 }
541
542 pub fn set_site_state(
544 &mut self,
545 x: usize,
546 y: usize,
547 amplitudes: [Complex64; 2],
548 ) -> QuantRS2Result<()> {
549 if x >= self.width || y >= self.height {
550 return Err(QuantRS2Error::InvalidInput(
551 "Site coordinates out of bounds".to_string(),
552 ));
553 }
554
555 let norm = (amplitudes[0].norm_sqr() + amplitudes[1].norm_sqr()).sqrt();
557 if norm < 1e-10 {
558 return Err(QuantRS2Error::InvalidInput(
559 "State cannot have zero norm".to_string(),
560 ));
561 }
562
563 self.state[[x, y, 0]] = amplitudes[0] / norm;
564 self.state[[x, y, 1]] = amplitudes[1] / norm;
565
566 Ok(())
567 }
568
569 pub fn get_site_state(&self, x: usize, y: usize) -> QuantRS2Result<[Complex64; 2]> {
571 if x >= self.width || y >= self.height {
572 return Err(QuantRS2Error::InvalidInput(
573 "Site coordinates out of bounds".to_string(),
574 ));
575 }
576
577 Ok([self.state[[x, y, 0]], self.state[[x, y, 1]]])
578 }
579
580 pub fn step(&mut self) -> QuantRS2Result<()> {
582 match self.qca_type {
583 QCAType::Margolus => self.step_margolus_2d(),
584 QCAType::Moore => self.step_moore_2d(),
585 QCAType::VonNeumann => self.step_von_neumann_2d(),
586 QCAType::Partitioned => self.step_partitioned_2d(),
587 }
588 }
589
590 fn step_margolus_2d(&mut self) -> QuantRS2Result<()> {
592 let rule_size = self.rule.neighborhood_size();
593 if rule_size != 16 {
594 return Err(QuantRS2Error::InvalidInput(
596 "2D Margolus requires 4-qubit rule".to_string(),
597 ));
598 }
599
600 let mut new_state = self.state.clone();
601 let x_offset = self.time_step % 2;
602 let y_offset = self.time_step % 2;
603
604 for i in (x_offset..self.width.saturating_sub(1)).step_by(2) {
606 for j in (y_offset..self.height.saturating_sub(1)).step_by(2) {
607 let block_state = self.get_block_state_vector(i, j)?;
608 let evolved = self.rule.apply(&block_state)?;
609
610 for (idx, &val) in evolved.iter().enumerate() {
612 let local_x = idx % 2;
613 let local_y = (idx / 2) % 2;
614 let qubit = (idx / 4) % 2;
615
616 if i + local_x < self.width && j + local_y < self.height {
617 new_state[[i + local_x, j + local_y, qubit]] = val;
618 }
619 }
620 }
621 }
622
623 self.state = new_state;
624 self.time_step += 1;
625 Ok(())
626 }
627
628 fn get_block_state_vector(
630 &self,
631 start_x: usize,
632 start_y: usize,
633 ) -> QuantRS2Result<Vec<Complex64>> {
634 let mut state_vector = vec![Complex64::new(0.0, 0.0); 16];
635
636 let sites = [
638 (start_x, start_y),
639 (start_x + 1, start_y),
640 (start_x, start_y + 1),
641 (start_x + 1, start_y + 1),
642 ];
643
644 for i in 0..2 {
646 for j in 0..2 {
647 for k in 0..2 {
648 for l in 0..2 {
649 let amp = self.state[[sites[0].0, sites[0].1, i]]
650 * self.state[[sites[1].0, sites[1].1, j]]
651 * self.state[[sites[2].0, sites[2].1, k]]
652 * self.state[[sites[3].0, sites[3].1, l]];
653
654 let idx = 8 * i + 4 * j + 2 * k + l;
655 state_vector[idx] = amp;
656 }
657 }
658 }
659 }
660
661 Ok(state_vector)
662 }
663
664 fn step_moore_2d(&mut self) -> QuantRS2Result<()> {
666 self.step_von_neumann_2d()
669 }
670
671 fn step_von_neumann_2d(&mut self) -> QuantRS2Result<()> {
673 let rule_size = self.rule.neighborhood_size();
674 if rule_size != 32 {
675 return Err(QuantRS2Error::InvalidInput(
677 "2D Von Neumann requires 5-qubit rule".to_string(),
678 ));
679 }
680
681 let mut new_state = self.state.clone();
682
683 for i in 0..self.width {
684 for j in 0..self.height {
685 let neighbors = self.get_von_neumann_neighbors(i, j);
687 let neighborhood_state = self.get_neighborhood_state_vector(&neighbors)?;
688
689 let evolved = self.rule.apply(&neighborhood_state)?;
691
692 new_state[[i, j, 0]] = evolved[16]; new_state[[i, j, 1]] = evolved[24]; }
696 }
697
698 self.state = new_state;
699 self.time_step += 1;
700 Ok(())
701 }
702
703 fn step_partitioned_2d(&mut self) -> QuantRS2Result<()> {
705 if self.time_step % 2 == 0 {
707 self.step_horizontal_pairs()
708 } else {
709 self.step_vertical_pairs()
710 }
711 }
712
713 fn step_horizontal_pairs(&mut self) -> QuantRS2Result<()> {
715 let rule_size = self.rule.neighborhood_size();
716 if rule_size != 4 {
717 return Err(QuantRS2Error::InvalidInput(
719 "Horizontal pairs require 2-qubit rule".to_string(),
720 ));
721 }
722
723 let mut new_state = self.state.clone();
724
725 for j in 0..self.height {
726 for i in (0..self.width.saturating_sub(1)).step_by(2) {
727 let pair_state = self.get_pair_state_vector_2d(i, j, i + 1, j)?;
728 let evolved = self.rule.apply(&pair_state)?;
729
730 new_state[[i, j, 0]] = evolved[0];
732 new_state[[i, j, 1]] = evolved[1];
733 new_state[[i + 1, j, 0]] = evolved[2];
734 new_state[[i + 1, j, 1]] = evolved[3];
735 }
736 }
737
738 self.state = new_state;
739 self.time_step += 1;
740 Ok(())
741 }
742
743 fn step_vertical_pairs(&mut self) -> QuantRS2Result<()> {
745 let rule_size = self.rule.neighborhood_size();
746 if rule_size != 4 {
747 return Err(QuantRS2Error::InvalidInput(
749 "Vertical pairs require 2-qubit rule".to_string(),
750 ));
751 }
752
753 let mut new_state = self.state.clone();
754
755 for i in 0..self.width {
756 for j in (0..self.height.saturating_sub(1)).step_by(2) {
757 let pair_state = self.get_pair_state_vector_2d(i, j, i, j + 1)?;
758 let evolved = self.rule.apply(&pair_state)?;
759
760 new_state[[i, j, 0]] = evolved[0];
762 new_state[[i, j, 1]] = evolved[1];
763 new_state[[i, j + 1, 0]] = evolved[2];
764 new_state[[i, j + 1, 1]] = evolved[3];
765 }
766 }
767
768 self.state = new_state;
769 self.time_step += 1;
770 Ok(())
771 }
772
773 fn get_von_neumann_neighbors(&self, x: usize, y: usize) -> Vec<(usize, usize)> {
775 vec![
776 (x, y), (self.get_neighbor_x(x, -1), y), (self.get_neighbor_x(x, 1), y), (x, self.get_neighbor_y(y, -1)), (x, self.get_neighbor_y(y, 1)), ]
782 }
783
784 fn get_neighbor_x(&self, x: usize, offset: isize) -> usize {
786 match self.boundary {
787 BoundaryCondition::Periodic => {
788 let new_x = x as isize + offset;
789 ((new_x % self.width as isize + self.width as isize) % self.width as isize) as usize
790 }
791 BoundaryCondition::Fixed | BoundaryCondition::Open => {
792 let new_x = x as isize + offset;
793 if new_x < 0 {
794 0
795 } else if new_x >= self.width as isize {
796 self.width - 1
797 } else {
798 new_x as usize
799 }
800 }
801 }
802 }
803
804 fn get_neighbor_y(&self, y: usize, offset: isize) -> usize {
806 match self.boundary {
807 BoundaryCondition::Periodic => {
808 let new_y = y as isize + offset;
809 ((new_y % self.height as isize + self.height as isize) % self.height as isize)
810 as usize
811 }
812 BoundaryCondition::Fixed | BoundaryCondition::Open => {
813 let new_y = y as isize + offset;
814 if new_y < 0 {
815 0
816 } else if new_y >= self.height as isize {
817 self.height - 1
818 } else {
819 new_y as usize
820 }
821 }
822 }
823 }
824
825 fn get_neighborhood_state_vector(
827 &self,
828 sites: &[(usize, usize)],
829 ) -> QuantRS2Result<Vec<Complex64>> {
830 let num_sites = sites.len();
831 let state_size = 1 << num_sites;
832 let mut state_vector = vec![Complex64::new(0.0, 0.0); state_size];
833
834 state_vector[0] = Complex64::new(1.0, 0.0);
837 Ok(state_vector)
838 }
839
840 fn get_pair_state_vector_2d(
842 &self,
843 x1: usize,
844 y1: usize,
845 x2: usize,
846 y2: usize,
847 ) -> QuantRS2Result<Vec<Complex64>> {
848 let amp1_0 = self.state[[x1, y1, 0]];
849 let amp1_1 = self.state[[x1, y1, 1]];
850 let amp2_0 = self.state[[x2, y2, 0]];
851 let amp2_1 = self.state[[x2, y2, 1]];
852
853 Ok(vec![
854 amp1_0 * amp2_0, amp1_0 * amp2_1, amp1_1 * amp2_0, amp1_1 * amp2_1, ])
859 }
860
861 pub fn probability_distribution(&self) -> Array3<f64> {
863 let mut probs = Array3::zeros((self.width, self.height, 2));
864
865 for i in 0..self.width {
866 for j in 0..self.height {
867 probs[[i, j, 0]] = self.state[[i, j, 0]].norm_sqr();
868 probs[[i, j, 1]] = self.state[[i, j, 1]].norm_sqr();
869 }
870 }
871
872 probs
873 }
874
875 pub fn magnetization(&self) -> f64 {
877 let mut total_magnetization = 0.0;
878
879 for i in 0..self.width {
880 for j in 0..self.height {
881 let prob_0 = self.state[[i, j, 0]].norm_sqr();
882 let prob_1 = self.state[[i, j, 1]].norm_sqr();
883 total_magnetization += prob_0 - prob_1; }
885 }
886
887 total_magnetization / (self.width * self.height) as f64
888 }
889}
890
891impl std::fmt::Debug for QuantumCellularAutomaton2D {
892 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
893 f.debug_struct("QuantumCellularAutomaton2D")
894 .field("width", &self.width)
895 .field("height", &self.height)
896 .field("boundary", &self.boundary)
897 .field("qca_type", &self.qca_type)
898 .field("time_step", &self.time_step)
899 .finish()
900 }
901}
902
903#[cfg(test)]
904mod tests {
905 use super::*;
906
907 #[test]
908 fn test_unitary_rule_creation() {
909 let hadamard = UnitaryRule::hadamard();
910 assert_eq!(hadamard.num_qubits, 1);
911 assert!(hadamard.is_reversible());
912
913 let cnot = UnitaryRule::cnot();
914 assert_eq!(cnot.num_qubits, 2);
915 assert!(cnot.is_reversible());
916 }
917
918 #[test]
919 fn test_1d_qca_initialization() {
920 let rule = Box::new(UnitaryRule::cnot());
921 let qca = QuantumCellularAutomaton1D::new(
922 10,
923 rule,
924 BoundaryCondition::Periodic,
925 QCAType::Partitioned,
926 );
927
928 assert_eq!(qca.num_sites, 10);
929 assert_eq!(qca.time_step, 0);
930
931 for i in 0..10 {
933 let state = qca.get_site_state(i).unwrap();
934 assert!((state[0] - Complex64::new(1.0, 0.0)).norm() < 1e-10);
935 assert!((state[1] - Complex64::new(0.0, 0.0)).norm() < 1e-10);
936 }
937 }
938
939 #[test]
940 fn test_1d_qca_evolution() {
941 let rule = Box::new(UnitaryRule::cnot());
942 let mut qca = QuantumCellularAutomaton1D::new(
943 4,
944 rule,
945 BoundaryCondition::Periodic,
946 QCAType::Partitioned,
947 );
948
949 qca.set_site_state(1, [Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)])
951 .unwrap();
952
953 qca.step().unwrap();
955 assert_eq!(qca.time_step, 1);
956
957 let probs = qca.site_probabilities();
959 assert!(probs.len() == 4);
960 }
961
962 #[test]
963 fn test_2d_qca_initialization() {
964 let rule = Box::new(UnitaryRule::cnot());
965 let qca = QuantumCellularAutomaton2D::new(
966 5,
967 5,
968 rule,
969 BoundaryCondition::Periodic,
970 QCAType::Margolus,
971 );
972
973 assert_eq!(qca.width, 5);
974 assert_eq!(qca.height, 5);
975 assert_eq!(qca.time_step, 0);
976
977 for i in 0..5 {
979 for j in 0..5 {
980 let state = qca.get_site_state(i, j).unwrap();
981 assert!((state[0] - Complex64::new(1.0, 0.0)).norm() < 1e-10);
982 assert!((state[1] - Complex64::new(0.0, 0.0)).norm() < 1e-10);
983 }
984 }
985 }
986
987 #[test]
988 fn test_entanglement_entropy() {
989 let rule = Box::new(UnitaryRule::cnot());
990 let mut qca = QuantumCellularAutomaton1D::new(
991 4,
992 rule,
993 BoundaryCondition::Periodic,
994 QCAType::Partitioned,
995 );
996
997 let sqrt2_inv = 1.0 / 2.0_f64.sqrt();
999 qca.set_site_state(
1000 0,
1001 [
1002 Complex64::new(sqrt2_inv, 0.0),
1003 Complex64::new(sqrt2_inv, 0.0),
1004 ],
1005 )
1006 .unwrap();
1007
1008 let entropy = qca.entanglement_entropy(0, 2).unwrap();
1009 assert!(entropy >= 0.0);
1010 }
1011
1012 #[test]
1013 fn test_correlation_function() {
1014 let rule = Box::new(UnitaryRule::cnot());
1015 let mut qca = QuantumCellularAutomaton1D::new(
1016 4,
1017 rule,
1018 BoundaryCondition::Periodic,
1019 QCAType::Partitioned,
1020 );
1021
1022 qca.set_site_state(0, [Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)])
1024 .unwrap(); qca.set_site_state(1, [Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)])
1026 .unwrap(); let correlation = qca.correlation(0, 1).unwrap();
1029 assert!((correlation - Complex64::new(-1.0, 0.0)).norm() < 1e-10);
1030 }
1031
1032 #[test]
1033 fn test_2d_magnetization() {
1034 let rule = Box::new(UnitaryRule::cnot());
1035 let mut qca = QuantumCellularAutomaton2D::new(
1036 3,
1037 3,
1038 rule,
1039 BoundaryCondition::Periodic,
1040 QCAType::Partitioned,
1041 );
1042
1043 let magnetization = qca.magnetization();
1045 assert!((magnetization - 1.0).abs() < 1e-10);
1046
1047 for i in 0..3 {
1049 for j in 0..3 {
1050 qca.set_site_state(i, j, [Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)])
1051 .unwrap();
1052 }
1053 }
1054
1055 let magnetization = qca.magnetization();
1056 assert!((magnetization - (-1.0)).abs() < 1e-10);
1057 }
1058
1059 #[test]
1060 fn test_toffoli_rule() {
1061 let toffoli = UnitaryRule::toffoli();
1062 assert_eq!(toffoli.num_qubits, 3);
1063 assert_eq!(toffoli.neighborhood_size(), 8);
1064
1065 let input = vec![
1067 Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0), ];
1076
1077 let output = toffoli.apply(&input).unwrap();
1078
1079 assert!((output[6] - Complex64::new(0.0, 0.0)).norm() < 1e-10);
1081 assert!((output[7] - Complex64::new(1.0, 0.0)).norm() < 1e-10);
1082 }
1083}