use ndarray::prelude::*;
use rand::prelude::SliceRandom;
use rand::Rng;
#[derive(Clone, Copy)]
pub enum UpdateRule {
Metropolis,
Glauber,
}
#[derive(Clone, Copy)]
pub enum SpinType {
SpinHalf,
}
#[derive(Clone, Copy)]
pub enum InitType {
Random,
AllUp,
}
pub struct Lattice2d {
pub dims: [usize; 2],
pub n_sites: i32, pub nodes: Array2<i32>, update_rule: UpdateRule,
pub spin_type: SpinType,
pub init_type: InitType,
pub j: f64, pub h: f64, pub beta: f64, }
impl Clone for Lattice2d {
fn clone(&self) -> Self {
Self::new(
self.dims,
self.update_rule,
self.spin_type,
self.init_type,
self.j,
self.h,
self.beta,
)
}
}
impl Lattice2d {
pub fn new(
dims: [usize; 2],
update_rule: UpdateRule,
spin_type: SpinType,
init_type: InitType,
j: f64,
h: f64,
beta: f64,
) -> Self {
let nodes: Array2<i32> = Lattice2d::init_spins(&init_type, &dims);
let (width, height) = nodes.dim();
Lattice2d {
dims: [width, height],
n_sites: width as i32 * height as i32,
nodes: nodes, update_rule: update_rule,
spin_type: spin_type,
init_type: init_type,
j: j,
h: h,
beta: beta,
}
}
pub fn new_basic(dims: [usize; 2]) -> Self {
Self::new(
dims,
UpdateRule::Metropolis,
SpinType::SpinHalf,
InitType::Random,
1.0f64,
0.0f64,
0.43f64,
)
}
fn init_spins(init_type: &InitType, dims: &[usize; 2]) -> Array2<i32> {
let nodes: Array2<i32>;
match init_type {
InitType::Random => {
let mut rng = rand::thread_rng();
nodes = Array2::from_shape_fn(*dims, |_| *[-1, 1].choose(&mut rng).unwrap());
}
InitType::AllUp => {
nodes = Array2::<i32>::ones(*dims);
}
}
return nodes;
}
pub fn reset_spins(&mut self) {
self.nodes = Lattice2d::init_spins(&self.init_type, &self.dims);
}
#[allow(non_snake_case)] fn get_dE(&self, idx0: usize, idx1: usize) -> f64 {
let neighbour_spin_sum: i32 = self.nodes[[idx0, (idx1 + 1) % self.dims[1]]]
+ self.nodes[[
idx0,
match idx1 {
0 => self.dims[1] - 1,
_ => idx1 - 1,
},
]]
+ self.nodes[[(idx0 + 1) % self.dims[0], idx1]]
+ self.nodes[[
match idx0 {
0 => self.dims[0] - 1,
_ => idx0 - 1,
},
idx1,
]];
2.0 * self.j * ((neighbour_spin_sum * self.nodes[[idx0, idx1]]) as f64)
+ self.h * (self.nodes[[idx0, idx1]] as f64)
}
pub fn update(&mut self) {
match self.update_rule {
UpdateRule::Metropolis => {
let mut rng = rand::thread_rng();
let idx0: usize = rng.gen::<usize>() % self.dims[0];
let idx1: usize = rng.gen::<usize>() % self.dims[1];
#[allow(non_snake_case)]
let dE: f64 = self.get_dE(idx0, idx1);
if dE > 0.0 {
let p: f64 = rng.gen::<f64>(); if p < (-self.beta * dE).exp() {
self.nodes[[idx0, idx1]] *= -1;
}
} else {
self.nodes[[idx0, idx1]] *= -1; }
}
UpdateRule::Glauber => {
panic!("Warning Glauber Rule not yet implemented");
}
}
}
pub fn update_n(&mut self, n: usize) {
for _ in 0..n {
self.update();
}
}
pub fn disp_terminal(&self) {
let mut string = "----------------\n".to_owned();
for idx0 in 0..self.dims[0] {
string = string + "|";
for idx1 in 0..self.dims[1] {
match self.nodes[[idx0, idx1]] {
-1 => {
string = string + " ";
}
1 => {
string = string + "#";
}
_ => {
panic!("Ising lattice is an array of -1s and 1s");
}
}
}
string = string + "|\n";
}
string = string + "---------------------";
println!("{}", string);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lattice_new_basic() {
let lattice = Lattice2d::new_basic([5, 10]);
println!("{}", lattice.nodes); assert_eq!(lattice.nodes.raw_dim(), lattice.dims);
assert_eq!(
lattice.n_sites,
lattice.dims[0] as i32 * lattice.dims[1] as i32
)
}
#[test]
fn test_lattice_new() {
let lattice = Lattice2d::new(
[7, 8],
UpdateRule::Metropolis,
SpinType::SpinHalf,
InitType::Random,
1.0, 0.0, 0.43, );
assert_eq!(lattice.nodes.raw_dim(), lattice.dims);
assert_eq!(
lattice.n_sites,
lattice.dims[0] as i32 * lattice.dims[1] as i32
)
}
#[test]
fn test_clone_lattice() {
let lattice = Lattice2d::new_basic([3, 3]);
let _newlat = lattice.clone();
}
#[test]
fn test_update_n() {
let mut lattice = Lattice2d::new_basic([5, 10]);
lattice.update_n(10);
}
#[test]
#[allow(non_snake_case)]
fn test_get_dE() {
let lattice = Lattice2d::new_basic([5, 6]);
let i0: usize = 0;
let i1: usize = 3;
let _dE: f64 = lattice.get_dE(i0, i1);
}
#[test]
fn test_init_spins() {
let nodes: Array2<i32> =
Lattice2d::init_spins(&InitType::Random, &[4 as usize, 5 as usize]);
let (width, height) = nodes.dim();
assert_eq!(width, 4usize);
assert_eq!(height, 5usize);
assert!(nodes[[3, 4]] == 1 || nodes[[3, 4]] == -1);
assert!(nodes[[0, 0]] == 1 || nodes[[0, 0]] == -1);
let nodes: Array2<i32> = Lattice2d::init_spins(&InitType::AllUp, &[2 as usize, 3 as usize]);
let (width, height) = nodes.dim();
assert_eq!(width, 2usize);
assert_eq!(height, 3usize);
assert_eq!(nodes[[1, 1]], 1i32);
}
#[test]
fn test_reset_spins() {
let mut lattice = Lattice2d::new_basic([5, 10]);
lattice.reset_spins(); }
#[test]
fn test_update_disp() {
let mut lattice = Lattice2d::new_basic([5, 5]);
lattice.disp_terminal();
for _ in 0..3 {
for _ in 0..1 {
lattice.update();
}
lattice.disp_terminal();
}
}
}