use crate::{
components::{
operator::Pauli,
pauli_string::{PauliString, SumOp},
},
errors::Error,
};
use num_complex::Complex;
use rayon::prelude::*;
pub fn ising_1d<const N: usize>(h: [f64; N], j: [f64; N], mu: f64) -> Result<SumOp, Error> {
if N < 2 {
return Err(Error::InvalidNumberOfInputs(N, 2));
}
let all_zero = h.iter().all(|&val| val == 0.0) && j.iter().all(|&val| val == 0.0);
if all_zero {
return Ok(SumOp::new(Vec::new()));
}
let chunk_size = std::cmp::max(1, N / rayon::current_num_threads());
let terms: Vec<PauliString> = (0..N)
.into_par_iter()
.chunks(chunk_size)
.flat_map(|chunk| {
let mut local_terms = Vec::new();
for i in chunk {
let j_val = j[i];
if j_val != 0.0 {
let coeff_1: Complex<f64> = Complex::new(j_val, 0.0) * -1.0;
let mut pauli_1: PauliString = PauliString::new(coeff_1);
pauli_1.add_op(i, Pauli::Z);
pauli_1.add_op((i + 1) % N, Pauli::Z);
local_terms.push(pauli_1);
}
let h_val = h[i];
if h_val != 0.0 {
let coeff_2: Complex<f64> = -1.0 * mu * Complex::new(h_val, 0.0);
let mut pauli_2: PauliString = PauliString::new(coeff_2);
pauli_2.add_op(i, Pauli::Z);
local_terms.push(pauli_2);
}
}
local_terms
})
.collect();
Ok(SumOp::new(terms))
}
pub fn ising_1d_uniform(n: usize, h: f64, j: f64, mu: f64) -> Result<SumOp, Error> {
if n < 2 {
return Err(Error::InvalidNumberOfInputs(n, 2));
}
if h == 0.0 && j == 0.0 {
return Ok(SumOp::new(Vec::new()));
}
let coeff_1: Complex<f64> = Complex::new(j, 0.0) * -1.0;
let coeff_2: Complex<f64> = -1.0 * mu * Complex::new(h, 0.0);
let include_j: bool = j != 0.0;
let include_h: bool = h != 0.0;
let chunk_size: usize = std::cmp::max(1, n / rayon::current_num_threads());
let terms: Vec<PauliString> = (0..n)
.into_par_iter()
.chunks(chunk_size)
.flat_map(|chunk| {
let mut local_terms = Vec::new();
for i in chunk {
if include_j {
let mut pauli_1: PauliString = PauliString::new(coeff_1);
pauli_1.add_op(i, Pauli::Z);
pauli_1.add_op((i + 1) % n, Pauli::Z);
local_terms.push(pauli_1);
}
if include_h {
let mut pauli_2: PauliString = PauliString::new(coeff_2);
pauli_2.add_op(i, Pauli::Z);
local_terms.push(pauli_2);
}
}
local_terms
})
.collect();
Ok(SumOp::new(terms))
}
pub fn ising_2d<const N: usize, const M: usize>(
h_param: [[f64; M]; N],
j_param: [[[f64; 2]; M]; N],
mu: f64,
) -> Result<SumOp, Error> {
if N < 2 {
return Err(Error::InvalidNumberOfInputs(N, 2));
} else if M < 2 {
return Err(Error::InvalidNumberOfInputs(M, 2));
}
let mut all_zero = true;
'outer: for r in 0..N {
for c in 0..M {
if h_param[r][c] != 0.0 || j_param[r][c][0] != 0.0 || j_param[r][c][1] != 0.0 {
all_zero = false;
break 'outer;
}
}
}
if all_zero {
return Ok(SumOp::new(Vec::new()));
}
let chunk_size: usize = std::cmp::max(1, (N * M) / rayon::current_num_threads());
let terms: Vec<PauliString> = (0..N*M)
.into_par_iter()
.chunks(chunk_size)
.flat_map(|chunk| {
let mut local_terms = Vec::new();
for idx in chunk {
let r: usize = idx / M;
let c: usize = idx % M;
let current_site_1d_idx: usize = idx;
let field_coeff_val: f64 = h_param[r][c];
if field_coeff_val != 0.0 {
let field_coeff: Complex<f64> = -1.0 * mu * Complex::new(field_coeff_val, 0.0);
let mut field_pauli_string: PauliString = PauliString::new(field_coeff);
field_pauli_string.add_op(current_site_1d_idx, Pauli::Z);
local_terms.push(field_pauli_string);
}
let vertical_coupling_val: f64 = j_param[r][c][0];
if vertical_coupling_val != 0.0 {
let vertical_coeff: Complex<f64> = Complex::new(vertical_coupling_val, 0.0) * -1.0;
let mut vertical_pauli_string: PauliString = PauliString::new(vertical_coeff);
vertical_pauli_string.add_op(current_site_1d_idx, Pauli::Z);
let next_row_site_1d_idx: usize = ((r + 1) % N) * M + c;
vertical_pauli_string.add_op(next_row_site_1d_idx, Pauli::Z);
local_terms.push(vertical_pauli_string);
}
let horizontal_coupling_val: f64 = j_param[r][c][1];
if horizontal_coupling_val != 0.0 {
let horizontal_coeff: Complex<f64> = Complex::new(horizontal_coupling_val, 0.0) * -1.0;
let mut horizontal_pauli_string: PauliString = PauliString::new(horizontal_coeff);
horizontal_pauli_string.add_op(current_site_1d_idx, Pauli::Z);
let next_col_site_1d_idx: usize = r * M + ((c + 1) % M);
horizontal_pauli_string.add_op(next_col_site_1d_idx, Pauli::Z);
local_terms.push(horizontal_pauli_string);
}
}
local_terms
})
.collect();
Ok(SumOp::new(terms))
}
pub fn ising_2d_uniform(n: usize, m: usize, h: f64, j: f64, mu: f64) -> Result<SumOp, Error> {
if n < 2 {
return Err(Error::InvalidNumberOfInputs(n, 2));
} else if m < 2 {
return Err(Error::InvalidNumberOfInputs(m, 2));
}
if h == 0.0 && j == 0.0 {
return Ok(SumOp::new(Vec::new()));
}
let field_coeff: Complex<f64> = -1.0 * mu * Complex::new(h, 0.0);
let coupling_coeff: Complex<f64> = Complex::new(j, 0.0) * -1.0;
let include_coupling: bool = j != 0.0;
let include_field: bool = h != 0.0;
let chunk_size: usize = std::cmp::max(1, (n * m) / rayon::current_num_threads());
let terms: Vec<PauliString> = (0..n*m)
.into_par_iter()
.chunks(chunk_size)
.flat_map(|chunk| {
let mut local_terms = Vec::with_capacity(chunk.len() * (if include_coupling { 3 } else { 1 }));
for idx in chunk {
let r: usize = idx / m;
let c: usize = idx % m;
let current_site_1d_idx: usize = idx;
if include_field {
let mut field_pauli_string: PauliString = PauliString::new(field_coeff);
field_pauli_string.add_op(current_site_1d_idx, Pauli::Z);
local_terms.push(field_pauli_string);
}
if include_coupling {
let mut vertical_pauli_string: PauliString = PauliString::new(coupling_coeff);
vertical_pauli_string.add_op(current_site_1d_idx, Pauli::Z);
let next_row_site_1d_idx: usize = ((r + 1) % n) * m + c;
vertical_pauli_string.add_op(next_row_site_1d_idx, Pauli::Z);
local_terms.push(vertical_pauli_string);
let mut horizontal_pauli_string: PauliString = PauliString::new(coupling_coeff);
horizontal_pauli_string.add_op(current_site_1d_idx, Pauli::Z);
let next_col_site_1d_idx: usize = r * m + ((c + 1) % m);
horizontal_pauli_string.add_op(next_col_site_1d_idx, Pauli::Z);
local_terms.push(horizontal_pauli_string);
}
}
local_terms
})
.collect();
Ok(SumOp::new(terms))
}