use crate::error::{QuantRS2Error, QuantRS2Result};
use scirs2_core::ndarray::{Array1, Array2, Array3};
use scirs2_core::Complex64;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QCAType {
Partitioned,
Margolus,
Moore,
VonNeumann,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BoundaryCondition {
Periodic,
Fixed,
Open,
}
pub trait QCARule {
fn apply(&self, neighborhood: &[Complex64]) -> QuantRS2Result<Vec<Complex64>>;
fn neighborhood_size(&self) -> usize;
fn is_reversible(&self) -> bool;
}
#[derive(Debug, Clone)]
pub struct UnitaryRule {
pub unitary: Array2<Complex64>,
pub num_qubits: usize,
}
impl UnitaryRule {
pub fn new(unitary: Array2<Complex64>) -> QuantRS2Result<Self> {
let (rows, cols) = unitary.dim();
if rows != cols {
return Err(QuantRS2Error::InvalidInput(
"Unitary matrix must be square".to_string(),
));
}
let conjugate_transpose = unitary.t().mapv(|x| x.conj());
let product = conjugate_transpose.dot(&unitary);
for i in 0..rows {
for j in 0..cols {
let expected = if i == j {
Complex64::new(1.0, 0.0)
} else {
Complex64::new(0.0, 0.0)
};
if (product[[i, j]] - expected).norm() > 1e-10 {
return Err(QuantRS2Error::InvalidInput(
"Matrix is not unitary".to_string(),
));
}
}
}
let num_qubits = (rows as f64).log2() as usize;
if 1 << num_qubits != rows {
return Err(QuantRS2Error::InvalidInput(
"Matrix dimension must be a power of 2".to_string(),
));
}
Ok(Self {
unitary,
num_qubits,
})
}
pub fn cnot() -> Self {
let unitary = Array2::from_shape_vec(
(4, 4),
vec![
Complex64::new(1.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),
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),
Complex64::new(0.0, 0.0),
Complex64::new(1.0, 0.0),
Complex64::new(0.0, 0.0),
],
)
.expect("CNOT matrix has valid 4x4 shape");
Self::new(unitary).expect("CNOT matrix is a valid unitary")
}
pub fn hadamard() -> Self {
let sqrt2_inv = 1.0 / 2.0_f64.sqrt();
let unitary = Array2::from_shape_vec(
(2, 2),
vec![
Complex64::new(sqrt2_inv, 0.0),
Complex64::new(sqrt2_inv, 0.0),
Complex64::new(sqrt2_inv, 0.0),
Complex64::new(-sqrt2_inv, 0.0),
],
)
.expect("Hadamard matrix has valid 2x2 shape");
Self::new(unitary).expect("Hadamard matrix is a valid unitary")
}
pub fn toffoli() -> Self {
let mut unitary = Array2::zeros((8, 8));
for i in 0..6 {
unitary[[i, i]] = Complex64::new(1.0, 0.0);
}
unitary[[6, 7]] = Complex64::new(1.0, 0.0);
unitary[[7, 6]] = Complex64::new(1.0, 0.0);
Self::new(unitary).expect("Toffoli matrix is a valid unitary")
}
}
impl QCARule for UnitaryRule {
fn apply(&self, neighborhood: &[Complex64]) -> QuantRS2Result<Vec<Complex64>> {
if neighborhood.len() != 1 << self.num_qubits {
return Err(QuantRS2Error::InvalidInput(
"Neighborhood size doesn't match rule dimension".to_string(),
));
}
let state = Array1::from_vec(neighborhood.to_vec());
let evolved = self.unitary.dot(&state);
Ok(evolved.to_vec())
}
fn neighborhood_size(&self) -> usize {
1 << self.num_qubits
}
fn is_reversible(&self) -> bool {
true }
}
pub struct QuantumCellularAutomaton1D {
pub num_sites: usize,
pub state: Array2<Complex64>,
rule: Box<dyn QCARule + Send + Sync>,
boundary: BoundaryCondition,
qca_type: QCAType,
pub time_step: usize,
}
impl QuantumCellularAutomaton1D {
pub fn new(
num_sites: usize,
rule: Box<dyn QCARule + Send + Sync>,
boundary: BoundaryCondition,
qca_type: QCAType,
) -> Self {
let mut state = Array2::zeros((num_sites, 2));
for i in 0..num_sites {
state[[i, 0]] = Complex64::new(1.0, 0.0); }
Self {
num_sites,
state,
rule,
boundary,
qca_type,
time_step: 0,
}
}
pub fn set_site_state(
&mut self,
site: usize,
amplitudes: [Complex64; 2],
) -> QuantRS2Result<()> {
if site >= self.num_sites {
return Err(QuantRS2Error::InvalidInput(
"Site index out of bounds".to_string(),
));
}
let norm = (amplitudes[0].norm_sqr() + amplitudes[1].norm_sqr()).sqrt();
if norm < 1e-10 {
return Err(QuantRS2Error::InvalidInput(
"State cannot have zero norm".to_string(),
));
}
self.state[[site, 0]] = amplitudes[0] / norm;
self.state[[site, 1]] = amplitudes[1] / norm;
Ok(())
}
pub fn get_site_state(&self, site: usize) -> QuantRS2Result<[Complex64; 2]> {
if site >= self.num_sites {
return Err(QuantRS2Error::InvalidInput(
"Site index out of bounds".to_string(),
));
}
Ok([self.state[[site, 0]], self.state[[site, 1]]])
}
pub fn initialize_random(
&mut self,
rng: &mut dyn scirs2_core::random::RngCore,
) -> QuantRS2Result<()> {
use scirs2_core::random::prelude::*;
for i in 0..self.num_sites {
let theta = rng.random_range(0.0..std::f64::consts::PI);
let phi = rng.random_range(0.0..2.0 * std::f64::consts::PI);
let amp0 = Complex64::new(theta.cos(), 0.0);
let amp1 = Complex64::new(theta.sin() * phi.cos(), theta.sin() * phi.sin());
self.set_site_state(i, [amp0, amp1])?;
}
Ok(())
}
pub fn step(&mut self) -> QuantRS2Result<()> {
match self.qca_type {
QCAType::Partitioned => self.step_partitioned(),
QCAType::Margolus => self.step_margolus(),
QCAType::Moore => self.step_moore(),
QCAType::VonNeumann => self.step_von_neumann(),
}
}
fn step_partitioned(&mut self) -> QuantRS2Result<()> {
let rule_size = self.rule.neighborhood_size();
let num_qubits = (rule_size as f64).log2() as usize;
if num_qubits != 2 {
return Err(QuantRS2Error::InvalidInput(
"Partitioned QCA currently supports only 2-qubit rules".to_string(),
));
}
let mut new_state = self.state.clone();
let offset = self.time_step % 2;
for i in (offset..self.num_sites.saturating_sub(1)).step_by(2) {
let j = (i + 1) % self.num_sites;
let neighborhood = self.get_pair_state_vector(i, j)?;
let evolved = self.rule.apply(&neighborhood)?;
new_state[[i, 0]] = evolved[0];
new_state[[i, 1]] = evolved[1];
new_state[[j, 0]] = evolved[2];
new_state[[j, 1]] = evolved[3];
}
self.state = new_state;
self.time_step += 1;
Ok(())
}
fn get_pair_state_vector(&self, site1: usize, site2: usize) -> QuantRS2Result<Vec<Complex64>> {
let amp1_0 = self.state[[site1, 0]];
let amp1_1 = self.state[[site1, 1]];
let amp2_0 = self.state[[site2, 0]];
let amp2_1 = self.state[[site2, 1]];
Ok(vec![
amp1_0 * amp2_0, amp1_0 * amp2_1, amp1_1 * amp2_0, amp1_1 * amp2_1, ])
}
fn step_margolus(&mut self) -> QuantRS2Result<()> {
self.step_partitioned()
}
fn step_moore(&mut self) -> QuantRS2Result<()> {
let rule_size = self.rule.neighborhood_size();
if rule_size != 8 {
return Err(QuantRS2Error::InvalidInput(
"Moore neighborhood requires 3-qubit rule".to_string(),
));
}
let mut new_state = self.state.clone();
for i in 0..self.num_sites {
let left = self.get_neighbor(i, -1);
let center = i;
let right = self.get_neighbor(i, 1);
let neighborhood = self.get_triple_state_vector(left, center, right)?;
let evolved = self.rule.apply(&neighborhood)?;
new_state[[center, 0]] = evolved[2]; new_state[[center, 1]] = evolved[6];
}
self.state = new_state;
self.time_step += 1;
Ok(())
}
fn step_von_neumann(&mut self) -> QuantRS2Result<()> {
self.step_moore()
}
const fn get_neighbor(&self, site: usize, offset: isize) -> usize {
match self.boundary {
BoundaryCondition::Periodic => {
let new_site = site as isize + offset;
((new_site % self.num_sites as isize + self.num_sites as isize)
% self.num_sites as isize) as usize
}
BoundaryCondition::Fixed | BoundaryCondition::Open => {
let new_site = site as isize + offset;
if new_site < 0 {
0
} else if new_site >= self.num_sites as isize {
self.num_sites - 1
} else {
new_site as usize
}
}
}
}
fn get_triple_state_vector(
&self,
site1: usize,
site2: usize,
site3: usize,
) -> QuantRS2Result<Vec<Complex64>> {
let mut state_vector = vec![Complex64::new(0.0, 0.0); 8];
for i in 0..2 {
for j in 0..2 {
for k in 0..2 {
let amp =
self.state[[site1, i]] * self.state[[site2, j]] * self.state[[site3, k]];
let idx = 4 * i + 2 * j + k;
state_vector[idx] = amp;
}
}
}
Ok(state_vector)
}
pub fn entanglement_entropy(
&self,
region_start: usize,
region_size: usize,
) -> QuantRS2Result<f64> {
if region_start + region_size > self.num_sites {
return Err(QuantRS2Error::InvalidInput(
"Region extends beyond lattice".to_string(),
));
}
let mut entropy = 0.0;
for i in region_start..region_start + region_size {
let p0 = self.state[[i, 0]].norm_sqr();
let p1 = self.state[[i, 1]].norm_sqr();
if p0 > 1e-12 {
entropy -= p0 * p0.ln();
}
if p1 > 1e-12 {
entropy -= p1 * p1.ln();
}
}
Ok(entropy)
}
pub fn site_probabilities(&self) -> Vec<[f64; 2]> {
self.state
.rows()
.into_iter()
.map(|row| [row[0].norm_sqr(), row[1].norm_sqr()])
.collect()
}
pub fn correlation(&self, site1: usize, site2: usize) -> QuantRS2Result<Complex64> {
if site1 >= self.num_sites || site2 >= self.num_sites {
return Err(QuantRS2Error::InvalidInput(
"Site index out of bounds".to_string(),
));
}
let z1 = self.state[[site1, 0]].norm_sqr() - self.state[[site1, 1]].norm_sqr();
let z2 = self.state[[site2, 0]].norm_sqr() - self.state[[site2, 1]].norm_sqr();
Ok(Complex64::new(z1 * z2, 0.0))
}
}
impl std::fmt::Debug for QuantumCellularAutomaton1D {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("QuantumCellularAutomaton1D")
.field("num_sites", &self.num_sites)
.field("boundary", &self.boundary)
.field("qca_type", &self.qca_type)
.field("time_step", &self.time_step)
.finish()
}
}
pub struct QuantumCellularAutomaton2D {
pub width: usize,
pub height: usize,
pub state: Array3<Complex64>,
rule: Box<dyn QCARule + Send + Sync>,
boundary: BoundaryCondition,
qca_type: QCAType,
pub time_step: usize,
}
impl QuantumCellularAutomaton2D {
pub fn new(
width: usize,
height: usize,
rule: Box<dyn QCARule + Send + Sync>,
boundary: BoundaryCondition,
qca_type: QCAType,
) -> Self {
let mut state = Array3::zeros((width, height, 2));
for i in 0..width {
for j in 0..height {
state[[i, j, 0]] = Complex64::new(1.0, 0.0); }
}
Self {
width,
height,
state,
rule,
boundary,
qca_type,
time_step: 0,
}
}
pub fn set_site_state(
&mut self,
x: usize,
y: usize,
amplitudes: [Complex64; 2],
) -> QuantRS2Result<()> {
if x >= self.width || y >= self.height {
return Err(QuantRS2Error::InvalidInput(
"Site coordinates out of bounds".to_string(),
));
}
let norm = (amplitudes[0].norm_sqr() + amplitudes[1].norm_sqr()).sqrt();
if norm < 1e-10 {
return Err(QuantRS2Error::InvalidInput(
"State cannot have zero norm".to_string(),
));
}
self.state[[x, y, 0]] = amplitudes[0] / norm;
self.state[[x, y, 1]] = amplitudes[1] / norm;
Ok(())
}
pub fn get_site_state(&self, x: usize, y: usize) -> QuantRS2Result<[Complex64; 2]> {
if x >= self.width || y >= self.height {
return Err(QuantRS2Error::InvalidInput(
"Site coordinates out of bounds".to_string(),
));
}
Ok([self.state[[x, y, 0]], self.state[[x, y, 1]]])
}
pub fn step(&mut self) -> QuantRS2Result<()> {
match self.qca_type {
QCAType::Margolus => self.step_margolus_2d(),
QCAType::Moore => self.step_moore_2d(),
QCAType::VonNeumann => self.step_von_neumann_2d(),
QCAType::Partitioned => self.step_partitioned_2d(),
}
}
fn step_margolus_2d(&mut self) -> QuantRS2Result<()> {
let rule_size = self.rule.neighborhood_size();
if rule_size != 16 {
return Err(QuantRS2Error::InvalidInput(
"2D Margolus requires 4-qubit rule".to_string(),
));
}
let mut new_state = self.state.clone();
let x_offset = self.time_step % 2;
let y_offset = self.time_step % 2;
for i in (x_offset..self.width.saturating_sub(1)).step_by(2) {
for j in (y_offset..self.height.saturating_sub(1)).step_by(2) {
let block_state = self.get_block_state_vector(i, j)?;
let evolved = self.rule.apply(&block_state)?;
for (idx, &val) in evolved.iter().enumerate() {
let local_x = idx % 2;
let local_y = (idx / 2) % 2;
let qubit = (idx / 4) % 2;
if i + local_x < self.width && j + local_y < self.height {
new_state[[i + local_x, j + local_y, qubit]] = val;
}
}
}
}
self.state = new_state;
self.time_step += 1;
Ok(())
}
fn get_block_state_vector(
&self,
start_x: usize,
start_y: usize,
) -> QuantRS2Result<Vec<Complex64>> {
let mut state_vector = vec![Complex64::new(0.0, 0.0); 16];
let sites = [
(start_x, start_y),
(start_x + 1, start_y),
(start_x, start_y + 1),
(start_x + 1, start_y + 1),
];
for i in 0..2 {
for j in 0..2 {
for k in 0..2 {
for l in 0..2 {
let amp = self.state[[sites[0].0, sites[0].1, i]]
* self.state[[sites[1].0, sites[1].1, j]]
* self.state[[sites[2].0, sites[2].1, k]]
* self.state[[sites[3].0, sites[3].1, l]];
let idx = 8 * i + 4 * j + 2 * k + l;
state_vector[idx] = amp;
}
}
}
}
Ok(state_vector)
}
fn step_moore_2d(&mut self) -> QuantRS2Result<()> {
self.step_von_neumann_2d()
}
fn step_von_neumann_2d(&mut self) -> QuantRS2Result<()> {
let rule_size = self.rule.neighborhood_size();
if rule_size != 32 {
return Err(QuantRS2Error::InvalidInput(
"2D Von Neumann requires 5-qubit rule".to_string(),
));
}
let mut new_state = self.state.clone();
for i in 0..self.width {
for j in 0..self.height {
let neighbors = self.get_von_neumann_neighbors(i, j);
let neighborhood_state = self.get_neighborhood_state_vector(&neighbors)?;
let evolved = self.rule.apply(&neighborhood_state)?;
new_state[[i, j, 0]] = evolved[16]; new_state[[i, j, 1]] = evolved[24]; }
}
self.state = new_state;
self.time_step += 1;
Ok(())
}
fn step_partitioned_2d(&mut self) -> QuantRS2Result<()> {
if self.time_step % 2 == 0 {
self.step_horizontal_pairs()
} else {
self.step_vertical_pairs()
}
}
fn step_horizontal_pairs(&mut self) -> QuantRS2Result<()> {
let rule_size = self.rule.neighborhood_size();
if rule_size != 4 {
return Err(QuantRS2Error::InvalidInput(
"Horizontal pairs require 2-qubit rule".to_string(),
));
}
let mut new_state = self.state.clone();
for j in 0..self.height {
for i in (0..self.width.saturating_sub(1)).step_by(2) {
let pair_state = self.get_pair_state_vector_2d(i, j, i + 1, j)?;
let evolved = self.rule.apply(&pair_state)?;
new_state[[i, j, 0]] = evolved[0];
new_state[[i, j, 1]] = evolved[1];
new_state[[i + 1, j, 0]] = evolved[2];
new_state[[i + 1, j, 1]] = evolved[3];
}
}
self.state = new_state;
self.time_step += 1;
Ok(())
}
fn step_vertical_pairs(&mut self) -> QuantRS2Result<()> {
let rule_size = self.rule.neighborhood_size();
if rule_size != 4 {
return Err(QuantRS2Error::InvalidInput(
"Vertical pairs require 2-qubit rule".to_string(),
));
}
let mut new_state = self.state.clone();
for i in 0..self.width {
for j in (0..self.height.saturating_sub(1)).step_by(2) {
let pair_state = self.get_pair_state_vector_2d(i, j, i, j + 1)?;
let evolved = self.rule.apply(&pair_state)?;
new_state[[i, j, 0]] = evolved[0];
new_state[[i, j, 1]] = evolved[1];
new_state[[i, j + 1, 0]] = evolved[2];
new_state[[i, j + 1, 1]] = evolved[3];
}
}
self.state = new_state;
self.time_step += 1;
Ok(())
}
fn get_von_neumann_neighbors(&self, x: usize, y: usize) -> Vec<(usize, usize)> {
vec![
(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)), ]
}
const fn get_neighbor_x(&self, x: usize, offset: isize) -> usize {
match self.boundary {
BoundaryCondition::Periodic => {
let new_x = x as isize + offset;
((new_x % self.width as isize + self.width as isize) % self.width as isize) as usize
}
BoundaryCondition::Fixed | BoundaryCondition::Open => {
let new_x = x as isize + offset;
if new_x < 0 {
0
} else if new_x >= self.width as isize {
self.width - 1
} else {
new_x as usize
}
}
}
}
const fn get_neighbor_y(&self, y: usize, offset: isize) -> usize {
match self.boundary {
BoundaryCondition::Periodic => {
let new_y = y as isize + offset;
((new_y % self.height as isize + self.height as isize) % self.height as isize)
as usize
}
BoundaryCondition::Fixed | BoundaryCondition::Open => {
let new_y = y as isize + offset;
if new_y < 0 {
0
} else if new_y >= self.height as isize {
self.height - 1
} else {
new_y as usize
}
}
}
}
fn get_neighborhood_state_vector(
&self,
sites: &[(usize, usize)],
) -> QuantRS2Result<Vec<Complex64>> {
let num_sites = sites.len();
let state_size = 1 << num_sites;
let mut state_vector = vec![Complex64::new(0.0, 0.0); state_size];
state_vector[0] = Complex64::new(1.0, 0.0);
Ok(state_vector)
}
fn get_pair_state_vector_2d(
&self,
x1: usize,
y1: usize,
x2: usize,
y2: usize,
) -> QuantRS2Result<Vec<Complex64>> {
let amp1_0 = self.state[[x1, y1, 0]];
let amp1_1 = self.state[[x1, y1, 1]];
let amp2_0 = self.state[[x2, y2, 0]];
let amp2_1 = self.state[[x2, y2, 1]];
Ok(vec![
amp1_0 * amp2_0, amp1_0 * amp2_1, amp1_1 * amp2_0, amp1_1 * amp2_1, ])
}
pub fn probability_distribution(&self) -> Array3<f64> {
let mut probs = Array3::zeros((self.width, self.height, 2));
for i in 0..self.width {
for j in 0..self.height {
probs[[i, j, 0]] = self.state[[i, j, 0]].norm_sqr();
probs[[i, j, 1]] = self.state[[i, j, 1]].norm_sqr();
}
}
probs
}
pub fn magnetization(&self) -> f64 {
let mut total_magnetization = 0.0;
for i in 0..self.width {
for j in 0..self.height {
let prob_0 = self.state[[i, j, 0]].norm_sqr();
let prob_1 = self.state[[i, j, 1]].norm_sqr();
total_magnetization += prob_0 - prob_1; }
}
total_magnetization / (self.width * self.height) as f64
}
}
impl std::fmt::Debug for QuantumCellularAutomaton2D {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("QuantumCellularAutomaton2D")
.field("width", &self.width)
.field("height", &self.height)
.field("boundary", &self.boundary)
.field("qca_type", &self.qca_type)
.field("time_step", &self.time_step)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_unitary_rule_creation() {
let hadamard = UnitaryRule::hadamard();
assert_eq!(hadamard.num_qubits, 1);
assert!(hadamard.is_reversible());
let cnot = UnitaryRule::cnot();
assert_eq!(cnot.num_qubits, 2);
assert!(cnot.is_reversible());
}
#[test]
fn test_1d_qca_initialization() {
let rule = Box::new(UnitaryRule::cnot());
let qca = QuantumCellularAutomaton1D::new(
10,
rule,
BoundaryCondition::Periodic,
QCAType::Partitioned,
);
assert_eq!(qca.num_sites, 10);
assert_eq!(qca.time_step, 0);
for i in 0..10 {
let state = qca
.get_site_state(i)
.expect("Site state should be retrievable");
assert!((state[0] - Complex64::new(1.0, 0.0)).norm() < 1e-10);
assert!((state[1] - Complex64::new(0.0, 0.0)).norm() < 1e-10);
}
}
#[test]
fn test_1d_qca_evolution() {
let rule = Box::new(UnitaryRule::cnot());
let mut qca = QuantumCellularAutomaton1D::new(
4,
rule,
BoundaryCondition::Periodic,
QCAType::Partitioned,
);
qca.set_site_state(1, [Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)])
.expect("Site state should be set successfully");
qca.step().expect("QCA evolution step should succeed");
assert_eq!(qca.time_step, 1);
let probs = qca.site_probabilities();
assert!(probs.len() == 4);
}
#[test]
fn test_2d_qca_initialization() {
let rule = Box::new(UnitaryRule::cnot());
let qca = QuantumCellularAutomaton2D::new(
5,
5,
rule,
BoundaryCondition::Periodic,
QCAType::Margolus,
);
assert_eq!(qca.width, 5);
assert_eq!(qca.height, 5);
assert_eq!(qca.time_step, 0);
for i in 0..5 {
for j in 0..5 {
let state = qca
.get_site_state(i, j)
.expect("Site state should be retrievable");
assert!((state[0] - Complex64::new(1.0, 0.0)).norm() < 1e-10);
assert!((state[1] - Complex64::new(0.0, 0.0)).norm() < 1e-10);
}
}
}
#[test]
fn test_entanglement_entropy() {
let rule = Box::new(UnitaryRule::cnot());
let mut qca = QuantumCellularAutomaton1D::new(
4,
rule,
BoundaryCondition::Periodic,
QCAType::Partitioned,
);
let sqrt2_inv = 1.0 / 2.0_f64.sqrt();
qca.set_site_state(
0,
[
Complex64::new(sqrt2_inv, 0.0),
Complex64::new(sqrt2_inv, 0.0),
],
)
.expect("Site state should be set successfully");
let entropy = qca
.entanglement_entropy(0, 2)
.expect("Entanglement entropy calculation should succeed");
assert!(entropy >= 0.0);
}
#[test]
fn test_correlation_function() {
let rule = Box::new(UnitaryRule::cnot());
let mut qca = QuantumCellularAutomaton1D::new(
4,
rule,
BoundaryCondition::Periodic,
QCAType::Partitioned,
);
qca.set_site_state(0, [Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)])
.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)])
.expect("Site 1 state should be set to |1>");
let correlation = qca
.correlation(0, 1)
.expect("Correlation calculation should succeed");
assert!((correlation - Complex64::new(-1.0, 0.0)).norm() < 1e-10);
}
#[test]
fn test_2d_magnetization() {
let rule = Box::new(UnitaryRule::cnot());
let mut qca = QuantumCellularAutomaton2D::new(
3,
3,
rule,
BoundaryCondition::Periodic,
QCAType::Partitioned,
);
let magnetization = qca.magnetization();
assert!((magnetization - 1.0).abs() < 1e-10);
for i in 0..3 {
for j in 0..3 {
qca.set_site_state(i, j, [Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)])
.expect("Site state should be set to |1>");
}
}
let magnetization = qca.magnetization();
assert!((magnetization - (-1.0)).abs() < 1e-10);
}
#[test]
fn test_toffoli_rule() {
let toffoli = UnitaryRule::toffoli();
assert_eq!(toffoli.num_qubits, 3);
assert_eq!(toffoli.neighborhood_size(), 8);
let input = vec![
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), ];
let output = toffoli
.apply(&input)
.expect("Toffoli rule application should succeed");
assert!((output[6] - Complex64::new(0.0, 0.0)).norm() < 1e-10);
assert!((output[7] - Complex64::new(1.0, 0.0)).norm() < 1e-10);
}
}