-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathising.cxx
216 lines (175 loc) · 5.96 KB
/
ising.cxx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#include <NumCpp.hpp>
#include <cstddef>
#include <functional>
#include <iostream>
#include <string>
using namespace nc;
// 2D coordinates representing a site in the spin lattice S.
#define SITE s[0], s[1]
// Spin lattice size
#define SZ N * N
// von Neumann neighborhood
#define n1 (s[0] + 1) % N, s[1]
#define n2 s[0], (s[1] + 1) % N
#define n3 (s[0] - 1) % N, s[1]
#define n4 s[0], (s[1] - 1) % N
// *** Global simulation constants ***
// Data columns (for writing to file)
enum DATA_COLS { TEMP, ORDER, CHI, CB, U };
// Simulation parameters
const unsigned int M = 20000;
const unsigned int therm = 1000;
const unsigned int MC = 1;
const std::string method1 = "metropolis";
const std::string method2 = "heatbath";
// Temperature steps
struct {
const double max = 5;
const double min = 0.1;
const double steps = 80;
} T;
// Measurements
const int n_meas = 5;
// ************************************
NdArray<double> ising(const int N, const double J, const double B,
const std::string method_opt);
// Metropolis-Hastings
void metropolis(NdArray<int> &S, const int N, double kBT_i, const double J,
const double B);
// Heat-bath
void heatbath(NdArray<int> &S, const int N, double kBT_i, const double J);
// Print data columns
void tofile(NdArray<double> &measurements, std::string file);
int main(int argc, char *argv[]) {
const auto input_method = std::string(argv[1]);
if (argc != 6) {
fprintf(stderr,
"USAGE: <metropolis/heatbath> <lattice dim> <J> <B> <filename>\n");
return EXIT_FAILURE;
} else if (!(input_method == method1 || input_method == method2)) {
fprintf(stderr, "Methods: <metropolis/heatbath>\n");
return EXIT_FAILURE;
}
// Input args
const int N = atoi(argv[2]); // Square lattice dims
const double J = atof(argv[3]); // Coupling strength
const double B = atof(argv[4]); // External B-field strength
const std::string file = argv[5]; // File to store data in
// Print columns
NdArray<double> data = ising(N, J, B, input_method);
tofile(data, file);
return 0;
}
// DONE function pointer for choosing method
NdArray<double> ising(const int N, const double J, const double B,
const std::string method_opt) {
// Set random seed for reproducibility
random::seed(1337);
double m0 = 0, m = 0, E0 = 0, E = 0, E2 = 0, m2 = 0, m4 = 0;
// Temperature from high to low
NdArray<double> kBT = fliplr(linspace(T.min, T.max, T.steps));
// Function to handle computation
// Simplified to using only variables in signature
// Mostly made for learning
std::function<void(NdArray<int> &, double)> computation;
// The two methods are checked during input before
// getting bound to constant args.
if (method_opt == method1) {
//f(a,b,c,d,e) -> [b,c,e]f(a,d)
computation = std::bind(&metropolis, std::placeholders::_1, N,
std::placeholders::_2, J, B);
} else {
//f(a,b,c,d) -> [b,d]f(a,c)
computation = std::bind(&heatbath, std::placeholders::_1, N,
std::placeholders::_2, J);
}
// Order parameter lambda function
auto order = [N](NdArray<int> &S) {
return abs(sum<int>(S).item()) / ((double)SZ);
};
// Energy lambda function
auto energy = [N, J, B](NdArray<int> &S) {
NdArray<int> nbrs = roll(S, 1, Axis::COL) + roll(S, -1, Axis::COL) +
roll(S, 1, Axis::ROW) + roll(S, -1, Axis::ROW);
return (-J * sum(matmul(S, nbrs)).item() - B * sum(S).item()) /
((double)SZ);
};
// Init spin matrix with random ICs
auto S = random::choice<int>({-1, 1}, SZ).reshape(N, N);
// Measurement matrix
auto measurements = NdArray<double>(kBT.shape().cols, n_meas);
for (size_t T_i = 0; T_i < kBT.shape().cols; T_i++) {
// Thermalization
for (size_t i = 0; i < therm * SZ; i++) {
computation(S, kBT[T_i]);
}
// Reset measurements
m = m2 = m4 = E = E2 = 0;
// Simulation and measurements
for (size_t i = 0; i < M; i++) {
for (size_t j = 0; j < MC * SZ; j++) {
computation(S, kBT[T_i]);
}
// Order param
m0 = order(S);
// Energy
E0 = energy(S);
// Accumulate
m += m0;
m2 += m0 * m0;
m4 += m0 * m0 * m0 * m0;
E += E0;
E2 += E0 * E0;
}
// Average over M samples
m = m / M;
m2 = m2 / M;
m4 = m4 / M;
E = E / M;
E2 = E2 / M;
// Add to measurements
measurements(T_i, TEMP) = kBT[T_i];
measurements(T_i, ORDER) = m;
measurements(T_i, CHI) = (m2 - m * m) / (kBT[T_i]);
measurements(T_i, CB) = (E2 - E * E) / (kBT[T_i] * kBT[T_i]);
measurements(T_i, U) = 1 - m4 / (3 * (m2 * m2));
}
return measurements;
}
// Metropolis-Hastings (implemented with external field)
void metropolis(NdArray<int> &S, const int N, double kBT_i, const double J,
const double B) {
NdArray<int> s = random::randInt({1, 2}, N);
int S_alpha_beta = S(SITE) * (S(n1) + S(n2) + S(n3) + S(n4));
double dE = 2.0 * J * S_alpha_beta + 2.0 * B * S(SITE);
if (dE <= 0 || random::rand<double>() < exp(-dE / kBT_i))
S(SITE) = -S(SITE);
}
// Heat-bath (not implemented with external field)
void heatbath(NdArray<int> &S, const int N, double kBT_i, const double J) {
NdArray<int> s = random::randInt({1, 2}, N);
int s_j = S(n1) + S(n2) + S(n3) + S(n4);
double p_i = 1.0 / (1.0 + exp(-2.0 * J * s_j / kBT_i));
// Always accept the change
S(SITE) = (random::rand<double>() < p_i) ? 1 : -1;
}
// Write columns to file (NumCPP has a tofile() function that can be reshaped
// during the data analysis)
void tofile(NdArray<double> &measurements, std::string file) {
std::ofstream out;
std::string path = "../data/";
out.open(path + file);
// Header
out << "temp "
<< "order "
<< "chi "
<< "cb "
<< "u" << std::endl;
// Data
for (size_t i = 0; i < measurements.shape().rows; i++) {
for (size_t j = 0; j < measurements.shape().cols; j++) {
out << measurements(i, j) << ' ';
}
out << std::endl;
}
}