1use lattice_core::Graph;
2
3#[derive(Default)]
4struct Observable {
5 count: usize,
6 sum: f64,
7 sum_sq: f64,
8}
9
10impl Observable {
11 fn add(&mut self, value: f64) {
12 self.count += 1;
13 self.sum += value;
14 self.sum_sq += value * value;
15 }
16
17 fn mean(&self) -> f64 {
18 if self.count == 0 {
19 0.0
20 } else {
21 self.sum / self.count as f64
22 }
23 }
24
25 fn error(&self) -> f64 {
26 if self.count < 2 {
27 0.0
28 } else {
29 let n = self.count as f64;
30 let mean = self.mean();
31 let var = (self.sum_sq / n) - (mean * mean);
32 (var.max(0.0) / (n - 1.0)).sqrt()
33 }
34 }
35}
36
37struct Lcg {
38 state: u64,
39}
40
41impl Lcg {
42 fn new(seed: u64) -> Self {
43 Self { state: seed }
44 }
45
46 fn next_f64(&mut self) -> f64 {
47 self.state = self
48 .state
49 .wrapping_mul(6364136223846793005)
50 .wrapping_add(1);
51 let value = self.state >> 11;
52 (value as f64) / ((1u64 << 53) as f64)
53 }
54}
55
56fn main() {
57 println!("Metropolis Algorithm for Classical Ferromagnetic Ising Model");
58
59 let beta = 1.0 / 2.269;
60
61 let dim = 2usize;
62 let length = 32usize;
63 let graph = Graph::simple(dim, length);
64
65 let mut rng = Lcg::new(12345);
66 let mut spins = vec![1.0f64; graph.num_sites()];
67
68 let mut energy = Observable::default();
69 let mut mag2 = Observable::default();
70
71 let sweeps = 65536usize;
72 let therm = sweeps / 8;
73
74 for mcs in 0..(therm + sweeps) {
75 for site in 0..graph.num_sites() {
76 let mut diff = 0.0;
77 for k in 0..graph.num_neighbors(site) {
78 diff += 2.0 * spins[site] * spins[graph.neighbor(site, k)];
79 }
80 if rng.next_f64() < (-beta * diff).exp() {
81 spins[site] = -spins[site];
82 }
83 }
84
85 if mcs > therm {
86 let mut ene = 0.0;
87 for bond in 0..graph.num_bonds() {
88 ene -= spins[graph.source(bond)] * spins[graph.target(bond)];
89 }
90 energy.add(ene / graph.num_sites() as f64);
91
92 let mag = spins.iter().sum::<f64>() / graph.num_sites() as f64;
93 mag2.add(mag * mag);
94 }
95 }
96
97 println!("dimension = {dim}");
98 println!("length = {length}");
99 println!("beta = {beta}");
100 println!(
101 "energy density = {} +/- {}",
102 energy.mean(),
103 energy.error()
104 );
105 println!(
106 "magnetization density^2 = {} +/- {}",
107 mag2.mean(),
108 mag2.error()
109 );
110}