1use crate::error::{QuantRS2Error, QuantRS2Result};
9use scirs2_core::ndarray::{Array1, Array2, Array3};
10use scirs2_core::Complex64;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum QCAType {
15 Partitioned,
17 Margolus,
19 Moore,
21 VonNeumann,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
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 .expect("CNOT matrix has valid 4x4 shape");
123
124 Self::new(unitary).expect("CNOT matrix is a valid unitary")
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 .expect("Hadamard matrix has valid 2x2 shape");
140
141 Self::new(unitary).expect("Hadamard matrix is a valid unitary")
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).expect("Toffoli matrix is a valid unitary")
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(
262 &mut self,
263 rng: &mut dyn scirs2_core::random::RngCore,
264 ) -> QuantRS2Result<()> {
265 use scirs2_core::random::prelude::*;
266
267 for i in 0..self.num_sites {
268 let theta = rng.gen_range(0.0..std::f64::consts::PI);
269 let phi = rng.gen_range(0.0..2.0 * std::f64::consts::PI);
270
271 let amp0 = Complex64::new(theta.cos(), 0.0);
272 let amp1 = Complex64::new(theta.sin() * phi.cos(), theta.sin() * phi.sin());
273
274 self.set_site_state(i, [amp0, amp1])?;
275 }
276
277 Ok(())
278 }
279
280 pub fn step(&mut self) -> QuantRS2Result<()> {
282 match self.qca_type {
283 QCAType::Partitioned => self.step_partitioned(),
284 QCAType::Margolus => self.step_margolus(),
285 QCAType::Moore => self.step_moore(),
286 QCAType::VonNeumann => self.step_von_neumann(),
287 }
288 }
289
290 fn step_partitioned(&mut self) -> QuantRS2Result<()> {
292 let rule_size = self.rule.neighborhood_size();
293 let num_qubits = (rule_size as f64).log2() as usize;
294
295 if num_qubits != 2 {
296 return Err(QuantRS2Error::InvalidInput(
297 "Partitioned QCA currently supports only 2-qubit rules".to_string(),
298 ));
299 }
300
301 let mut new_state = self.state.clone();
302 let offset = self.time_step % 2;
303
304 for i in (offset..self.num_sites.saturating_sub(1)).step_by(2) {
306 let j = (i + 1) % self.num_sites;
307
308 let neighborhood = self.get_pair_state_vector(i, j)?;
310
311 let evolved = self.rule.apply(&neighborhood)?;
313
314 new_state[[i, 0]] = evolved[0];
316 new_state[[i, 1]] = evolved[1];
317 new_state[[j, 0]] = evolved[2];
318 new_state[[j, 1]] = evolved[3];
319 }
320
321 self.state = new_state;
322 self.time_step += 1;
323 Ok(())
324 }
325
326 fn get_pair_state_vector(&self, site1: usize, site2: usize) -> QuantRS2Result<Vec<Complex64>> {
328 let amp1_0 = self.state[[site1, 0]];
329 let amp1_1 = self.state[[site1, 1]];
330 let amp2_0 = self.state[[site2, 0]];
331 let amp2_1 = self.state[[site2, 1]];
332
333 Ok(vec![
335 amp1_0 * amp2_0, amp1_0 * amp2_1, amp1_1 * amp2_0, amp1_1 * amp2_1, ])
340 }
341
342 fn step_margolus(&mut self) -> QuantRS2Result<()> {
344 self.step_partitioned()
346 }
347
348 fn step_moore(&mut self) -> QuantRS2Result<()> {
350 let rule_size = self.rule.neighborhood_size();
351 if rule_size != 8 {
352 return Err(QuantRS2Error::InvalidInput(
354 "Moore neighborhood requires 3-qubit rule".to_string(),
355 ));
356 }
357
358 let mut new_state = self.state.clone();
359
360 for i in 0..self.num_sites {
361 let left = self.get_neighbor(i, -1);
362 let center = i;
363 let right = self.get_neighbor(i, 1);
364
365 let neighborhood = self.get_triple_state_vector(left, center, right)?;
367
368 let evolved = self.rule.apply(&neighborhood)?;
370
371 new_state[[center, 0]] = evolved[2]; new_state[[center, 1]] = evolved[6];
374 }
375
376 self.state = new_state;
377 self.time_step += 1;
378 Ok(())
379 }
380
381 fn step_von_neumann(&mut self) -> QuantRS2Result<()> {
383 self.step_moore()
385 }
386
387 const fn get_neighbor(&self, site: usize, offset: isize) -> usize {
389 match self.boundary {
390 BoundaryCondition::Periodic => {
391 let new_site = site as isize + offset;
392 ((new_site % self.num_sites as isize + self.num_sites as isize)
393 % self.num_sites as isize) as usize
394 }
395 BoundaryCondition::Fixed | BoundaryCondition::Open => {
396 let new_site = site as isize + offset;
397 if new_site < 0 {
398 0
399 } else if new_site >= self.num_sites as isize {
400 self.num_sites - 1
401 } else {
402 new_site as usize
403 }
404 }
405 }
406 }
407
408 fn get_triple_state_vector(
410 &self,
411 site1: usize,
412 site2: usize,
413 site3: usize,
414 ) -> QuantRS2Result<Vec<Complex64>> {
415 let mut state_vector = vec![Complex64::new(0.0, 0.0); 8];
416
417 for i in 0..2 {
419 for j in 0..2 {
420 for k in 0..2 {
421 let amp =
422 self.state[[site1, i]] * self.state[[site2, j]] * self.state[[site3, k]];
423 let idx = 4 * i + 2 * j + k;
424 state_vector[idx] = amp;
425 }
426 }
427 }
428
429 Ok(state_vector)
430 }
431
432 pub fn entanglement_entropy(
434 &self,
435 region_start: usize,
436 region_size: usize,
437 ) -> QuantRS2Result<f64> {
438 if region_start + region_size > self.num_sites {
439 return Err(QuantRS2Error::InvalidInput(
440 "Region extends beyond lattice".to_string(),
441 ));
442 }
443
444 let mut entropy = 0.0;
447
448 for i in region_start..region_start + region_size {
449 let p0 = self.state[[i, 0]].norm_sqr();
450 let p1 = self.state[[i, 1]].norm_sqr();
451
452 if p0 > 1e-12 {
453 entropy -= p0 * p0.ln();
454 }
455 if p1 > 1e-12 {
456 entropy -= p1 * p1.ln();
457 }
458 }
459
460 Ok(entropy)
461 }
462
463 pub fn site_probabilities(&self) -> Vec<[f64; 2]> {
465 self.state
466 .rows()
467 .into_iter()
468 .map(|row| [row[0].norm_sqr(), row[1].norm_sqr()])
469 .collect()
470 }
471
472 pub fn correlation(&self, site1: usize, site2: usize) -> QuantRS2Result<Complex64> {
474 if site1 >= self.num_sites || site2 >= self.num_sites {
475 return Err(QuantRS2Error::InvalidInput(
476 "Site index out of bounds".to_string(),
477 ));
478 }
479
480 let z1 = self.state[[site1, 0]].norm_sqr() - self.state[[site1, 1]].norm_sqr();
482 let z2 = self.state[[site2, 0]].norm_sqr() - self.state[[site2, 1]].norm_sqr();
483
484 Ok(Complex64::new(z1 * z2, 0.0))
485 }
486}
487
488impl std::fmt::Debug for QuantumCellularAutomaton1D {
489 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
490 f.debug_struct("QuantumCellularAutomaton1D")
491 .field("num_sites", &self.num_sites)
492 .field("boundary", &self.boundary)
493 .field("qca_type", &self.qca_type)
494 .field("time_step", &self.time_step)
495 .finish()
496 }
497}
498
499pub struct QuantumCellularAutomaton2D {
501 pub width: usize,
503 pub height: usize,
505 pub state: Array3<Complex64>,
507 rule: Box<dyn QCARule + Send + Sync>,
509 boundary: BoundaryCondition,
511 qca_type: QCAType,
513 pub time_step: usize,
515}
516
517impl QuantumCellularAutomaton2D {
518 pub fn new(
520 width: usize,
521 height: usize,
522 rule: Box<dyn QCARule + Send + Sync>,
523 boundary: BoundaryCondition,
524 qca_type: QCAType,
525 ) -> Self {
526 let mut state = Array3::zeros((width, height, 2));
528 for i in 0..width {
529 for j in 0..height {
530 state[[i, j, 0]] = Complex64::new(1.0, 0.0); }
532 }
533
534 Self {
535 width,
536 height,
537 state,
538 rule,
539 boundary,
540 qca_type,
541 time_step: 0,
542 }
543 }
544
545 pub fn set_site_state(
547 &mut self,
548 x: usize,
549 y: usize,
550 amplitudes: [Complex64; 2],
551 ) -> QuantRS2Result<()> {
552 if x >= self.width || y >= self.height {
553 return Err(QuantRS2Error::InvalidInput(
554 "Site coordinates out of bounds".to_string(),
555 ));
556 }
557
558 let norm = (amplitudes[0].norm_sqr() + amplitudes[1].norm_sqr()).sqrt();
560 if norm < 1e-10 {
561 return Err(QuantRS2Error::InvalidInput(
562 "State cannot have zero norm".to_string(),
563 ));
564 }
565
566 self.state[[x, y, 0]] = amplitudes[0] / norm;
567 self.state[[x, y, 1]] = amplitudes[1] / norm;
568
569 Ok(())
570 }
571
572 pub fn get_site_state(&self, x: usize, y: usize) -> QuantRS2Result<[Complex64; 2]> {
574 if x >= self.width || y >= self.height {
575 return Err(QuantRS2Error::InvalidInput(
576 "Site coordinates out of bounds".to_string(),
577 ));
578 }
579
580 Ok([self.state[[x, y, 0]], self.state[[x, y, 1]]])
581 }
582
583 pub fn step(&mut self) -> QuantRS2Result<()> {
585 match self.qca_type {
586 QCAType::Margolus => self.step_margolus_2d(),
587 QCAType::Moore => self.step_moore_2d(),
588 QCAType::VonNeumann => self.step_von_neumann_2d(),
589 QCAType::Partitioned => self.step_partitioned_2d(),
590 }
591 }
592
593 fn step_margolus_2d(&mut self) -> QuantRS2Result<()> {
595 let rule_size = self.rule.neighborhood_size();
596 if rule_size != 16 {
597 return Err(QuantRS2Error::InvalidInput(
599 "2D Margolus requires 4-qubit rule".to_string(),
600 ));
601 }
602
603 let mut new_state = self.state.clone();
604 let x_offset = self.time_step % 2;
605 let y_offset = self.time_step % 2;
606
607 for i in (x_offset..self.width.saturating_sub(1)).step_by(2) {
609 for j in (y_offset..self.height.saturating_sub(1)).step_by(2) {
610 let block_state = self.get_block_state_vector(i, j)?;
611 let evolved = self.rule.apply(&block_state)?;
612
613 for (idx, &val) in evolved.iter().enumerate() {
615 let local_x = idx % 2;
616 let local_y = (idx / 2) % 2;
617 let qubit = (idx / 4) % 2;
618
619 if i + local_x < self.width && j + local_y < self.height {
620 new_state[[i + local_x, j + local_y, qubit]] = val;
621 }
622 }
623 }
624 }
625
626 self.state = new_state;
627 self.time_step += 1;
628 Ok(())
629 }
630
631 fn get_block_state_vector(
633 &self,
634 start_x: usize,
635 start_y: usize,
636 ) -> QuantRS2Result<Vec<Complex64>> {
637 let mut state_vector = vec![Complex64::new(0.0, 0.0); 16];
638
639 let sites = [
641 (start_x, start_y),
642 (start_x + 1, start_y),
643 (start_x, start_y + 1),
644 (start_x + 1, start_y + 1),
645 ];
646
647 for i in 0..2 {
649 for j in 0..2 {
650 for k in 0..2 {
651 for l in 0..2 {
652 let amp = self.state[[sites[0].0, sites[0].1, i]]
653 * self.state[[sites[1].0, sites[1].1, j]]
654 * self.state[[sites[2].0, sites[2].1, k]]
655 * self.state[[sites[3].0, sites[3].1, l]];
656
657 let idx = 8 * i + 4 * j + 2 * k + l;
658 state_vector[idx] = amp;
659 }
660 }
661 }
662 }
663
664 Ok(state_vector)
665 }
666
667 fn step_moore_2d(&mut self) -> QuantRS2Result<()> {
669 self.step_von_neumann_2d()
672 }
673
674 fn step_von_neumann_2d(&mut self) -> QuantRS2Result<()> {
676 let rule_size = self.rule.neighborhood_size();
677 if rule_size != 32 {
678 return Err(QuantRS2Error::InvalidInput(
680 "2D Von Neumann requires 5-qubit rule".to_string(),
681 ));
682 }
683
684 let mut new_state = self.state.clone();
685
686 for i in 0..self.width {
687 for j in 0..self.height {
688 let neighbors = self.get_von_neumann_neighbors(i, j);
690 let neighborhood_state = self.get_neighborhood_state_vector(&neighbors)?;
691
692 let evolved = self.rule.apply(&neighborhood_state)?;
694
695 new_state[[i, j, 0]] = evolved[16]; new_state[[i, j, 1]] = evolved[24]; }
699 }
700
701 self.state = new_state;
702 self.time_step += 1;
703 Ok(())
704 }
705
706 fn step_partitioned_2d(&mut self) -> QuantRS2Result<()> {
708 if self.time_step % 2 == 0 {
710 self.step_horizontal_pairs()
711 } else {
712 self.step_vertical_pairs()
713 }
714 }
715
716 fn step_horizontal_pairs(&mut self) -> QuantRS2Result<()> {
718 let rule_size = self.rule.neighborhood_size();
719 if rule_size != 4 {
720 return Err(QuantRS2Error::InvalidInput(
722 "Horizontal pairs require 2-qubit rule".to_string(),
723 ));
724 }
725
726 let mut new_state = self.state.clone();
727
728 for j in 0..self.height {
729 for i in (0..self.width.saturating_sub(1)).step_by(2) {
730 let pair_state = self.get_pair_state_vector_2d(i, j, i + 1, j)?;
731 let evolved = self.rule.apply(&pair_state)?;
732
733 new_state[[i, j, 0]] = evolved[0];
735 new_state[[i, j, 1]] = evolved[1];
736 new_state[[i + 1, j, 0]] = evolved[2];
737 new_state[[i + 1, j, 1]] = evolved[3];
738 }
739 }
740
741 self.state = new_state;
742 self.time_step += 1;
743 Ok(())
744 }
745
746 fn step_vertical_pairs(&mut self) -> QuantRS2Result<()> {
748 let rule_size = self.rule.neighborhood_size();
749 if rule_size != 4 {
750 return Err(QuantRS2Error::InvalidInput(
752 "Vertical pairs require 2-qubit rule".to_string(),
753 ));
754 }
755
756 let mut new_state = self.state.clone();
757
758 for i in 0..self.width {
759 for j in (0..self.height.saturating_sub(1)).step_by(2) {
760 let pair_state = self.get_pair_state_vector_2d(i, j, i, j + 1)?;
761 let evolved = self.rule.apply(&pair_state)?;
762
763 new_state[[i, j, 0]] = evolved[0];
765 new_state[[i, j, 1]] = evolved[1];
766 new_state[[i, j + 1, 0]] = evolved[2];
767 new_state[[i, j + 1, 1]] = evolved[3];
768 }
769 }
770
771 self.state = new_state;
772 self.time_step += 1;
773 Ok(())
774 }
775
776 fn get_von_neumann_neighbors(&self, x: usize, y: usize) -> Vec<(usize, usize)> {
778 vec![
779 (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)), ]
785 }
786
787 const fn get_neighbor_x(&self, x: usize, offset: isize) -> usize {
789 match self.boundary {
790 BoundaryCondition::Periodic => {
791 let new_x = x as isize + offset;
792 ((new_x % self.width as isize + self.width as isize) % self.width as isize) as usize
793 }
794 BoundaryCondition::Fixed | BoundaryCondition::Open => {
795 let new_x = x as isize + offset;
796 if new_x < 0 {
797 0
798 } else if new_x >= self.width as isize {
799 self.width - 1
800 } else {
801 new_x as usize
802 }
803 }
804 }
805 }
806
807 const fn get_neighbor_y(&self, y: usize, offset: isize) -> usize {
809 match self.boundary {
810 BoundaryCondition::Periodic => {
811 let new_y = y as isize + offset;
812 ((new_y % self.height as isize + self.height as isize) % self.height as isize)
813 as usize
814 }
815 BoundaryCondition::Fixed | BoundaryCondition::Open => {
816 let new_y = y as isize + offset;
817 if new_y < 0 {
818 0
819 } else if new_y >= self.height as isize {
820 self.height - 1
821 } else {
822 new_y as usize
823 }
824 }
825 }
826 }
827
828 fn get_neighborhood_state_vector(
830 &self,
831 sites: &[(usize, usize)],
832 ) -> QuantRS2Result<Vec<Complex64>> {
833 let num_sites = sites.len();
834 let state_size = 1 << num_sites;
835 let mut state_vector = vec![Complex64::new(0.0, 0.0); state_size];
836
837 state_vector[0] = Complex64::new(1.0, 0.0);
840 Ok(state_vector)
841 }
842
843 fn get_pair_state_vector_2d(
845 &self,
846 x1: usize,
847 y1: usize,
848 x2: usize,
849 y2: usize,
850 ) -> QuantRS2Result<Vec<Complex64>> {
851 let amp1_0 = self.state[[x1, y1, 0]];
852 let amp1_1 = self.state[[x1, y1, 1]];
853 let amp2_0 = self.state[[x2, y2, 0]];
854 let amp2_1 = self.state[[x2, y2, 1]];
855
856 Ok(vec![
857 amp1_0 * amp2_0, amp1_0 * amp2_1, amp1_1 * amp2_0, amp1_1 * amp2_1, ])
862 }
863
864 pub fn probability_distribution(&self) -> Array3<f64> {
866 let mut probs = Array3::zeros((self.width, self.height, 2));
867
868 for i in 0..self.width {
869 for j in 0..self.height {
870 probs[[i, j, 0]] = self.state[[i, j, 0]].norm_sqr();
871 probs[[i, j, 1]] = self.state[[i, j, 1]].norm_sqr();
872 }
873 }
874
875 probs
876 }
877
878 pub fn magnetization(&self) -> f64 {
880 let mut total_magnetization = 0.0;
881
882 for i in 0..self.width {
883 for j in 0..self.height {
884 let prob_0 = self.state[[i, j, 0]].norm_sqr();
885 let prob_1 = self.state[[i, j, 1]].norm_sqr();
886 total_magnetization += prob_0 - prob_1; }
888 }
889
890 total_magnetization / (self.width * self.height) as f64
891 }
892}
893
894impl std::fmt::Debug for QuantumCellularAutomaton2D {
895 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
896 f.debug_struct("QuantumCellularAutomaton2D")
897 .field("width", &self.width)
898 .field("height", &self.height)
899 .field("boundary", &self.boundary)
900 .field("qca_type", &self.qca_type)
901 .field("time_step", &self.time_step)
902 .finish()
903 }
904}
905
906#[cfg(test)]
907mod tests {
908 use super::*;
909
910 #[test]
911 fn test_unitary_rule_creation() {
912 let hadamard = UnitaryRule::hadamard();
913 assert_eq!(hadamard.num_qubits, 1);
914 assert!(hadamard.is_reversible());
915
916 let cnot = UnitaryRule::cnot();
917 assert_eq!(cnot.num_qubits, 2);
918 assert!(cnot.is_reversible());
919 }
920
921 #[test]
922 fn test_1d_qca_initialization() {
923 let rule = Box::new(UnitaryRule::cnot());
924 let qca = QuantumCellularAutomaton1D::new(
925 10,
926 rule,
927 BoundaryCondition::Periodic,
928 QCAType::Partitioned,
929 );
930
931 assert_eq!(qca.num_sites, 10);
932 assert_eq!(qca.time_step, 0);
933
934 for i in 0..10 {
936 let state = qca
937 .get_site_state(i)
938 .expect("Site state should be retrievable");
939 assert!((state[0] - Complex64::new(1.0, 0.0)).norm() < 1e-10);
940 assert!((state[1] - Complex64::new(0.0, 0.0)).norm() < 1e-10);
941 }
942 }
943
944 #[test]
945 fn test_1d_qca_evolution() {
946 let rule = Box::new(UnitaryRule::cnot());
947 let mut qca = QuantumCellularAutomaton1D::new(
948 4,
949 rule,
950 BoundaryCondition::Periodic,
951 QCAType::Partitioned,
952 );
953
954 qca.set_site_state(1, [Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)])
956 .expect("Site state should be set successfully");
957
958 qca.step().expect("QCA evolution step should succeed");
960 assert_eq!(qca.time_step, 1);
961
962 let probs = qca.site_probabilities();
964 assert!(probs.len() == 4);
965 }
966
967 #[test]
968 fn test_2d_qca_initialization() {
969 let rule = Box::new(UnitaryRule::cnot());
970 let qca = QuantumCellularAutomaton2D::new(
971 5,
972 5,
973 rule,
974 BoundaryCondition::Periodic,
975 QCAType::Margolus,
976 );
977
978 assert_eq!(qca.width, 5);
979 assert_eq!(qca.height, 5);
980 assert_eq!(qca.time_step, 0);
981
982 for i in 0..5 {
984 for j in 0..5 {
985 let state = qca
986 .get_site_state(i, j)
987 .expect("Site state should be retrievable");
988 assert!((state[0] - Complex64::new(1.0, 0.0)).norm() < 1e-10);
989 assert!((state[1] - Complex64::new(0.0, 0.0)).norm() < 1e-10);
990 }
991 }
992 }
993
994 #[test]
995 fn test_entanglement_entropy() {
996 let rule = Box::new(UnitaryRule::cnot());
997 let mut qca = QuantumCellularAutomaton1D::new(
998 4,
999 rule,
1000 BoundaryCondition::Periodic,
1001 QCAType::Partitioned,
1002 );
1003
1004 let sqrt2_inv = 1.0 / 2.0_f64.sqrt();
1006 qca.set_site_state(
1007 0,
1008 [
1009 Complex64::new(sqrt2_inv, 0.0),
1010 Complex64::new(sqrt2_inv, 0.0),
1011 ],
1012 )
1013 .expect("Site state should be set successfully");
1014
1015 let entropy = qca
1016 .entanglement_entropy(0, 2)
1017 .expect("Entanglement entropy calculation should succeed");
1018 assert!(entropy >= 0.0);
1019 }
1020
1021 #[test]
1022 fn test_correlation_function() {
1023 let rule = Box::new(UnitaryRule::cnot());
1024 let mut qca = QuantumCellularAutomaton1D::new(
1025 4,
1026 rule,
1027 BoundaryCondition::Periodic,
1028 QCAType::Partitioned,
1029 );
1030
1031 qca.set_site_state(0, [Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)])
1033 .expect("Site 0 state should be set to |0>"); qca.set_site_state(1, [Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)])
1035 .expect("Site 1 state should be set to |1>"); let correlation = qca
1038 .correlation(0, 1)
1039 .expect("Correlation calculation should succeed");
1040 assert!((correlation - Complex64::new(-1.0, 0.0)).norm() < 1e-10);
1041 }
1042
1043 #[test]
1044 fn test_2d_magnetization() {
1045 let rule = Box::new(UnitaryRule::cnot());
1046 let mut qca = QuantumCellularAutomaton2D::new(
1047 3,
1048 3,
1049 rule,
1050 BoundaryCondition::Periodic,
1051 QCAType::Partitioned,
1052 );
1053
1054 let magnetization = qca.magnetization();
1056 assert!((magnetization - 1.0).abs() < 1e-10);
1057
1058 for i in 0..3 {
1060 for j in 0..3 {
1061 qca.set_site_state(i, j, [Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)])
1062 .expect("Site state should be set to |1>");
1063 }
1064 }
1065
1066 let magnetization = qca.magnetization();
1067 assert!((magnetization - (-1.0)).abs() < 1e-10);
1068 }
1069
1070 #[test]
1071 fn test_toffoli_rule() {
1072 let toffoli = UnitaryRule::toffoli();
1073 assert_eq!(toffoli.num_qubits, 3);
1074 assert_eq!(toffoli.neighborhood_size(), 8);
1075
1076 let input = vec![
1078 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), ];
1087
1088 let output = toffoli
1089 .apply(&input)
1090 .expect("Toffoli rule application should succeed");
1091
1092 assert!((output[6] - Complex64::new(0.0, 0.0)).norm() < 1e-10);
1094 assert!((output[7] - Complex64::new(1.0, 0.0)).norm() < 1e-10);
1095 }
1096}