print the acceptance
[qmc.git] / main.cpp
blob3ecb6b89cff92e5e9624573299e43f78889d00d0
1 #include <iostream>
2 #include <fstream>
3 #include <string>
4 #include <vector>
5 #include <cstdlib>
6 #include <cmath>
7 #include <ctime>
8 extern "C" {
9 #include <lua.h>
10 #include <lualib.h>
11 #include <lauxlib.h>
14 #define PI 3.141592653589792
15 #define DEFAULT_SIZE 10
16 #define MAX(a, b) ((a)>(b)?(a):(b))
17 #define MIN(a, b) ((a)<(b)?(a):(b))
19 using namespace std;
21 // End of forward decl
23 class SpinChain {
24 private:
25 public:
26 struct Change {
27 int i;
28 int j;
31 SpinChain () : m_spins(DEFAULT_SIZE) {}
32 SpinChain (int size) : m_spins(size) {}
33 SpinChain (const SpinChain& other) : m_spins(other.m_spins) {}
35 Change trialChange () {
36 const int L = size();
37 Change ret;
38 do {
39 ret.i = (rand()/(RAND_MAX+1.0))*L;
40 ret.j = (rand()/(RAND_MAX+1.0))*(L-1);
41 if (ret.j>=ret.i) ret.j = (ret.j+1)%L;
42 } while (parallel(ret.i, ret.j));
43 return ret;
46 SpinChain tryChange (const Change& change) const {
47 SpinChain ret(*this);
48 ret.m_spins[change.i] = !ret.m_spins[change.i];
49 ret.m_spins[change.j] = !ret.m_spins[change.j];
50 return ret;
53 SpinChain& applyChange (const Change& change) {
54 this->m_spins[change.i] = !this->m_spins[change.i];
55 this->m_spins[change.j] = !this->m_spins[change.j];
56 return *this;
59 int size () const { return m_spins.size(); }
60 int at (int i) const { return m_spins[i]?1:-1; }
61 bool parallel (int i, int j) const { return m_spins[i] == m_spins[j]; }
62 int magnetization () const {
63 int m = 0;
64 int L = m_spins.size();
65 for (int i=0;i<L;i++) {
66 m += m_spins[i]?1:-1;
68 return m;
71 static SpinChain up (int size = DEFAULT_SIZE) {
72 SpinChain ret(size);
73 for (int i=0;i<size;i++) {
74 ret.m_spins[i] = true;
76 return ret;
79 static SpinChain down (int size = DEFAULT_SIZE) {
80 SpinChain ret(size);
81 for (int i=0;i<size;i++) {
82 ret.m_spins[i] = false;
84 return ret;
87 static SpinChain neel (int size = DEFAULT_SIZE) {
88 SpinChain ret(size);
89 for (int i=0;i<size;i++) {
90 ret.m_spins[i] = i%2;
92 return ret;
95 static SpinChain half (int size = DEFAULT_SIZE) {
96 SpinChain ret(size);
97 for (int i=0;i<size;i++) {
98 ret.m_spins[i] = 2*i < size;
100 return ret;
103 static SpinChain random (int size = DEFAULT_SIZE) {
104 SpinChain ret(size);
105 for (int i=0;i<size;i++) {
106 ret.m_spins[i] = rand()/(RAND_MAX+1.0) < 0.5;
108 return ret;
111 static SpinChain fromString (const char *s, int size) {
112 SpinChain ret(size);
113 for (int i=0;i<size;i++) {
114 bool c;
115 switch (s[i]) {
116 case '1':
117 c = true;
118 break;
119 case '0':
120 c = false;
121 break;
123 ret.m_spins[i] = c;
125 return ret;
128 friend ostream& operator << (ostream&, const SpinChain&);
129 const SpinChain& operator = (const SpinChain& other) { m_spins = other.m_spins; return *this; }
130 bool operator == (const SpinChain& other) { return m_spins == other.m_spins; }
131 SpinChain& swap (SpinChain& other) { m_spins.swap(other.m_spins); return *this; }
133 private:
134 std::vector<bool> m_spins;
137 ostream& operator << (ostream& out, const SpinChain& s) {
138 for (std::vector<bool>::const_iterator iter = s.m_spins.begin();iter != s.m_spins.end();iter++) {
139 out << *iter;
141 return out;
144 class VarProb {
145 private:
146 // distance between the two spins in the lattice
147 static double _distance (double i, double j) { return 2.0*sin(PI*abs(i-j)); }
148 // coefficient between the two spins in the lattice
149 static double _V_ij (double i, double j) { return 2.0*log(_distance(i, j)); }
150 // (-1)^(# of spin ups in even indices)
151 static int _sign (const SpinChain& s) {
152 int ret = 1;
153 const int L = s.size();
154 for (int i=0;i<L;i+=2) {
155 ret *= s.at(i)?-1:1;
157 return ret;
159 // ratio between the signs of the two states
160 static int _signRatio (const SpinChain::Change& c) {
161 return (c.i%2)==(c.j%2)?1:-1; // each flip on even site bears a sign change
163 public:
164 typedef std::vector<double> Helper;
166 VarProb() : alpha(1.0) {}
167 VarProb(double a) : alpha(a) {}
169 double compute (const SpinChain& state) const {
170 const int L = state.size();
171 double x = 0.0;
172 int ups = 0;
173 for (int i=0;i<L;i++) {
174 ups += state.at(i);
175 for (int j=i+1;j<L;j++) {
176 double d = _V_ij(double(i)/L, double(j)/L);
177 x += d*(state.parallel(i, j)?1.0:-1.0);
180 if (ups!=0) return 0.0;
181 return alpha*x;
184 double diff (const SpinChain& state, const SpinChain::Change& c) const {
185 double ret = 0.0;
186 const int L = state.size();
187 const int i = MAX(c.i, c.j);
188 const int j = MIN(c.i, c.j);
189 if (state.parallel(i, j)) return 0.0;
190 for (int k=0;k<j;k++) {
191 double di = _V_ij(double(k)/L, double(i)/L);
192 double dj = _V_ij(double(k)/L, double(j)/L);
193 ret += di*(state.parallel(k, i)?-1.0:1.0);
194 ret += dj*(state.parallel(k, j)?-1.0:1.0);
196 for (int k=j+1;k<i;k++) {
197 double di = _V_ij(double(k)/L, double(i)/L);
198 double dj = _V_ij(double(k)/L, double(j)/L);
199 ret += di*(state.parallel(k, i)?-1.0:1.0);
200 ret += dj*(state.parallel(k, j)?-1.0:1.0);
202 for (int k=i+1;k<L;k++) {
203 double di = _V_ij(double(k)/L, double(i)/L);
204 double dj = _V_ij(double(k)/L, double(j)/L);
205 ret += di*(state.parallel(k, i)?-1.0:1.0);
206 ret += dj*(state.parallel(k, j)?-1.0:1.0);
208 return 2.0 * alpha * ret;
211 double ratio (const SpinChain& s, const SpinChain::Change& c) const {
212 const double exponent = diff(s, c);
213 return _signRatio(c)*exp(exponent);
216 double diffHelper (const Helper& h, const SpinChain::Change& c) const {
217 const vector<double>& v = h;
218 const int L = v.size();
219 double d = _V_ij(double(c.i)/L, double(c.j)/L);
220 // compensate double counting: i, j are surely antiparallel, so had a -1 sign
221 // they are counted once in v[i] and once in v[j]
222 // with the wrong sign
223 double x = 2.0 * d;
224 return -2.0* alpha * (v[c.i]+v[c.j]+x);
226 double ratioHelper (const Helper& h, const SpinChain::Change& c) const {
227 const double exponent = diffHelper(h, c);
228 return _signRatio(c)*exp(exponent);
230 void applyChangeHelper (const SpinChain& s, Helper& h, const SpinChain::Change& c) const {
231 vector<double>& v = h;
232 const int L = v.size();
233 const int i = MAX(c.i, c.j);
234 const int j = MIN(c.i, c.j);
235 v[i] = -v[i];
236 v[j] = -v[j];
237 for (int k=0;k<j;k++) {
238 double di = _V_ij(double(k)/L, double(i)/L);
239 double dj = _V_ij(double(k)/L, double(j)/L);
240 v[k] -= 2.0*di*(s.parallel(k, i)?-1.0:1.0);
241 v[k] -= 2.0*dj*(s.parallel(k, j)?-1.0:1.0);
243 for (int k=j+1;k<i;k++) {
244 double di = _V_ij(double(k)/L, double(i)/L);
245 double dj = _V_ij(double(k)/L, double(j)/L);
246 v[k] -= 2.0*di*(s.parallel(k, i)?-1.0:1.0);
247 v[k] -= 2.0*dj*(s.parallel(k, j)?-1.0:1.0);
249 for (int k=i+1;k<L;k++) {
250 double di = _V_ij(double(k)/L, double(i)/L);
251 double dj = _V_ij(double(k)/L, double(j)/L);
252 v[k] -= 2.0*di*(s.parallel(k, i)?-1.0:1.0);
253 v[k] -= 2.0*dj*(s.parallel(k, j)?-1.0:1.0);
255 double d = _V_ij(double(c.i)/L, double(c.j)/L);
256 v[i] -= 2.0*d;
257 v[j] -= 2.0*d;
259 void computeHelper (const SpinChain& s, Helper& h) {
260 const int L = s.size();
261 vector<double>& v = h;
262 if (v.size()<L) v.resize(L);
263 for (int i=0;i<L;i++) {
264 double x = 0.0;
265 for (int j=0;j<i;j++) {
266 double d = _V_ij(double(i)/L, double(j)/L);
267 x += d*(s.parallel(i, j)?1.0:-1.0);
269 for (int j=i+1;j<L;j++) {
270 double d = _V_ij(double(i)/L, double(j)/L);
271 x += d*(s.parallel(i, j)?1.0:-1.0);
273 v[i] = x;
277 private:
278 double alpha;
281 class Energy {
282 private:
283 double _H_i (const SpinChain& s, const VarProb& p, int i) const {
284 double self = 0.0;
285 double other = 0.0;
286 const int L = s.size();
287 if (s.parallel(i, (i+1)%L)) {
288 self += D*J;
289 } else {
290 SpinChain::Change flip = { i, (i+1)%L };
291 self -= D*J;
292 other += 0.5*J*(p.ratio(s, flip));
294 return (0.25*self)+other;
297 double _H_i (const SpinChain& s, const VarProb::Helper& h, const VarProb& p, int i) const {
298 double self = 0.0;
299 double other = 0.0;
300 const int L = h.size();
301 if (s.parallel(i, (i+1)%L)) {
302 self += D*J;
303 } else {
304 SpinChain::Change flip = { i, (i+1)%L };
305 self -= D*J;
306 other += 0.5*J*(p.ratioHelper(h, flip));
308 return (0.25*self)+other;
310 public:
311 typedef double ValueType;
313 Energy() : J(1.0), D(1.0) {}
314 Energy(double j) : J(j), D(1.0) {}
315 Energy(double j, double d) : J(j), D(d) {}
317 double compute (const SpinChain& s, const VarProb& p) const {
318 double ret = 0.0;
319 const int L = s.size();
320 for (int i=0;i<L;i++) {
321 ret += _H_i (s, p, i);
323 return ret;
326 double compute (const SpinChain& s, const VarProb::Helper& h, const VarProb& p) const {
327 double ret = 0.0;
328 const int L = s.size();
329 for (int i=0;i<L;i++) {
330 ret += _H_i (s, h, p, i);
332 return ret;
335 private:
336 double J;
337 double D;
340 template <typename Probability, typename State>
341 class QMC {
342 private:
343 void _recompute () {
344 m_probability.computeHelper(m_state, m_helper);
346 public:
348 QMC (const Probability& p, const State& s) : m_probability(p), m_state(s) {
349 _recompute();
352 typedef typename State::Change Change;
353 typedef typename Probability::Helper Helper;
355 const State& state () const { return m_state; }
356 void setState (const State& s) { m_state = s; _recompute(); }
357 const Probability& probability () const { return m_probability; }
358 void setProbability (const Probability& p) { m_probability = p; _recompute(); }
359 const Change& lastChange () const { return last_change; }
360 const Helper& helper () const { return m_helper; }
362 bool step() {
363 bool ret = false; // whether it's accepted
364 Change trial = m_state.trialChange(); // description of a trial step
365 double pchange_h = m_probability.ratioHelper(m_helper, trial);
366 double a = pchange_h*pchange_h; // pchange is in fact related to an amplitude
367 if (a>(rand()/(RAND_MAX+1.0))) {
368 // accept
369 m_state.applyChange(trial);
370 m_probability.applyChangeHelper(m_state, m_helper, trial); // XXX: leave after applyChange()
371 last_change = trial;
372 ret = true;
373 } else {
374 // reject
375 ret = false;
377 return ret;
379 private:
380 State m_state;
381 Probability m_probability;
382 Change last_change;
383 Helper m_helper;
386 void print_help () {
389 void print_state (const SpinChain& s) { cout << s << endl; }
391 void load_file (int& niter, int& nspins, double& alpha, SpinChain& init, const char* fn) {
392 lua_State *L = lua_open();
393 luaL_openlibs(L);
394 lua_pushnil(L);
395 lua_setglobal(L, "debug");
396 lua_pushnil(L);
397 lua_setglobal(L, "io");
398 luaL_dofile(L, fn);
399 lua_getglobal(L, "qmc_result");
400 if (!lua_isnil(L, -1)) {
401 lua_getfield(L, -1, "iterations");
402 niter = lua_tointeger(L, -1);
403 lua_pop(L, 1);
404 lua_getfield(L, -1, "alpha");
405 alpha = lua_tonumber(L, -1);
406 lua_pop(L, 1);
407 lua_getfield(L, -1, "final_state");
408 init = SpinChain::fromString(lua_tostring(L, -1), lua_objlen(L, -1));
409 nspins = init.size();
410 lua_pop(L, 1);
412 lua_pop(L, 1);
413 lua_close(L);
416 int main (int argc, char **argv) {
417 bool append = false;
418 ofstream out;
419 srand(time(NULL));
420 string filename("");
421 string summaryname("");
422 int nspins = 20;
423 int niter = 1e3;
424 double alpha = 0.3;
425 SpinChain init = SpinChain::neel(nspins);
426 for (int i=1;i<argc;i++) {
427 if (argv[i][0]!='-') {
428 print_help();
429 exit(1);
431 switch (argv[i][1]) {
432 case 'O':
433 append = true;
434 case 'o':
435 filename = argv[++i];
436 break;
437 case 'r':
438 srand(atoi(argv[++i]));
439 break;
440 case 'i':
441 niter = atoi(argv[++i]);
442 break;
443 case 'a':
444 alpha = atof(argv[++i]);
445 break;
446 case 'n':
447 nspins = atoi(argv[++i]);
448 init = SpinChain::half(nspins);
449 break;
450 case 'c':
451 summaryname = argv[++i];
452 load_file(niter, nspins, alpha, init, argv[i]);
453 break;
454 case 'h':
455 case 'H':
456 print_help();
457 exit(0);
458 break;
459 default:
460 print_help();
461 exit(1);
462 break;
466 if (filename!="") out.open(filename.c_str(), append?ofstream::app:ofstream::out);
467 QMC<VarProb, SpinChain> qmc(VarProb(alpha), init);
468 Energy en;
470 int nacc = 0;
471 bool accepted = true; // first value is always accepted
472 double esum = 0.0;
473 double e = 0.0;
474 vector<double> csum(nspins/2, 0.0);
475 vector<double> c(nspins/2, 0.0);
476 for (int i=1;i<=niter;i++) {
477 if (!accepted) {
478 // e still contains the correct value
479 } else {
480 const SpinChain& s = qmc.state();
481 e = en.compute(s, qmc.helper(), qmc.probability());
482 c[0] = s.at(0);
483 for (int j=1;j<nspins/2;j++) {
484 c[j] = s.parallel(0, j)?1.0:-1.0;
487 esum += e;
488 for (int j=0;j<nspins/2;j++) {
489 csum[j] += c[j];
491 accepted = qmc.step();
492 nacc += accepted?1:0;
493 if (out.is_open()) out << i << " " << double(nacc)/(i) << " " << e/nspins << " " << esum/(i*nspins) << "\n";
495 ofstream sfile;
496 if (summaryname!="") sfile.open(summaryname.c_str(), ofstream::app);
497 ostream &summary = (sfile.is_open())?sfile:cout;
498 summary << "qmc_result = {\n";
499 summary << "\tinitial_state = \"" << init << "\",\n";
500 summary << "\tfinal_state = \"" << qmc.state() << "\",\n";
501 summary << "\talpha = " << alpha << ",\n";
502 summary << "\tenergy_sum = " << esum << ", -- average per spin: " << (esum/(niter*nspins)) << "\n";
503 summary << "\titerations = " << niter << ",\n";
504 summary << "\tacceptances = " << nacc << ",\n";
505 summary << "\tmagnetization_sum = " << csum[0] << ",\n";
506 summary << "\tcorrelation_sum = { " << (niter-csum[0]*csum[0]/niter) << ", ";
507 for (int j=1;j<nspins/2;j++) {
508 summary << csum[j] << ", ";
510 summary << "},\n";
511 summary << "}\n";
512 summary << "qmcdata = qmcdata or {}\n";
513 summary << "table.insert(qmcdata, qmc_result)\n" << endl;
514 return 0;