2 * Copyright (c) 2009 Mauro Iazzi
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without
7 * restriction, including without limitation the rights to use,
8 * copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
35 #define PI 3.141592653589792
36 #define DEFAULT_SIZE 10
37 #define MAX(a, b) ((a)>(b)?(a):(b))
38 #define MIN(a, b) ((a)<(b)?(a):(b))
41 // End of forward decl
51 SpinChain () : m_spins(DEFAULT_SIZE
) {}
52 SpinChain (int size
) : m_spins(size
) {}
53 SpinChain (const SpinChain
& other
) : m_spins(other
.m_spins
) {}
55 Change
trialChange () {
59 ret
.i
= (rand()/(RAND_MAX
+1.0))*L
;
60 //ret.j = (rand()/(RAND_MAX+1.0))*(L-1);
61 //if (ret.j>=ret.i) ret.j = (ret.j+1)%L;
62 ret
.j
= (ret
.i
+ 1) % L
;
63 } while (parallel(ret
.i
, ret
.j
));
67 SpinChain
tryChange (const Change
& change
) const {
69 ret
.m_spins
[change
.i
] = !ret
.m_spins
[change
.i
];
70 ret
.m_spins
[change
.j
] = !ret
.m_spins
[change
.j
];
74 SpinChain
& applyChange (const Change
& change
) {
75 this->m_spins
[change
.i
] = !this->m_spins
[change
.i
];
76 this->m_spins
[change
.j
] = !this->m_spins
[change
.j
];
80 int size () const { return m_spins
.size(); }
81 int sigma (int i
) const { return m_spins
[i
]?1:-1; }
82 bool at (int i
) const { return m_spins
[i
]; }
83 bool parallel (int i
, int j
) const { return m_spins
[i
] == m_spins
[j
]; }
84 int magnetization () const {
86 int L
= m_spins
.size();
87 for (int i
=0;i
<L
;i
++) {
93 static SpinChain
up (int size
= DEFAULT_SIZE
) {
95 for (int i
=0;i
<size
;i
++) {
96 ret
.m_spins
[i
] = true;
101 static SpinChain
down (int size
= DEFAULT_SIZE
) {
103 for (int i
=0;i
<size
;i
++) {
104 ret
.m_spins
[i
] = false;
109 static SpinChain
neel (int size
= DEFAULT_SIZE
) {
111 for (int i
=0;i
<size
;i
++) {
112 ret
.m_spins
[i
] = i
%2;
117 static SpinChain
half (int size
= DEFAULT_SIZE
) {
119 for (int i
=0;i
<size
;i
++) {
120 ret
.m_spins
[i
] = 2*i
< size
;
125 static SpinChain
random (int size
= DEFAULT_SIZE
) {
127 for (int i
=0;i
<size
;i
++) {
128 ret
.m_spins
[i
] = rand()/(RAND_MAX
+1.0) < 0.5;
133 static SpinChain
halfRandom (int size
= DEFAULT_SIZE
) {
135 for (int i
=0;i
<size
/2;i
++) {
136 ret
.m_spins
[i
] = rand()/(RAND_MAX
+1.0) < 0.5;
137 ret
.m_spins
[size
-1-i
] = !ret
.m_spins
[i
];
142 static SpinChain
fromString (const char *s
, int size
) {
144 for (int i
=0;i
<size
;i
++) {
167 const std::string
& toString () const {
168 const int L
= size();
170 for (int i
=0;i
<L
;i
++) {
171 t
[i
] = at(i
)?'1':'0';
176 friend std::ostream
& operator << (std::ostream
&, const SpinChain
&);
177 const SpinChain
& operator = (const SpinChain
& other
) { m_spins
= other
.m_spins
; return *this; }
178 bool operator == (const SpinChain
& other
) { return m_spins
== other
.m_spins
; }
179 SpinChain
& swap (SpinChain
& other
) { m_spins
.swap(other
.m_spins
); return *this; }
182 static std::string t
;
183 std::vector
<bool> m_spins
;
186 std::string
SpinChain::t
;
188 std::ostream
& operator << (std::ostream
& out
, const SpinChain
& s
) {
189 out
<< s
.toString ();
195 // distance between the two spins in the lattice
196 static double _distance (double i
, double j
) { return 2.0*std::sin(PI
*std::abs(i
-j
)); }
197 // coefficient between the two spins in the lattice
198 static double _V_ij (double i
, double j
) { return 2.0*std::log(_distance(i
, j
)); }
199 // (-1)^(# of spin ups in even indices)
200 static int _sign (const SpinChain
& s
) {
202 const int L
= s
.size();
203 for (int i
=0;i
<L
;i
+=2) {
208 // ratio between the signs of the two states
209 static int _signRatio (const SpinChain::Change
& c
) {
210 return (c
.i
%2)==(c
.j
%2)?1:-1; // each flip on even site bears a sign change
213 typedef SpinChain State
;
215 std::vector
<double> V
;
216 std::vector
<double> v_ij
;
219 VarProb() : alpha(1.0) {}
220 VarProb(double a
) : alpha(a
) {}
222 double compute (const SpinChain
& state
) const {
223 const int L
= state
.size();
226 for (int i
=0;i
<L
;i
++) {
227 ups
+= state
.sigma(i
);
228 for (int j
=i
+1;j
<L
;j
++) {
229 double d
= _V_ij(double(i
)/L
, double(j
)/L
);
230 x
+= d
*(state
.parallel(i
, j
)?1.0:-1.0);
233 if (ups
!=0) return 0.0;
237 double diff (const SpinChain
& state
, const SpinChain::Change
& c
) const {
239 const int L
= state
.size();
240 const int i
= MAX(c
.i
, c
.j
);
241 const int j
= MIN(c
.i
, c
.j
);
242 if (state
.parallel(i
, j
)) return 0.0;
243 for (int k
=0;k
<j
;k
++) {
244 double di
= _V_ij(double(k
)/L
, double(i
)/L
);
245 double dj
= _V_ij(double(k
)/L
, double(j
)/L
);
246 ret
+= di
*(state
.parallel(k
, i
)?-1.0:1.0);
247 ret
+= dj
*(state
.parallel(k
, j
)?-1.0:1.0);
249 for (int k
=j
+1;k
<i
;k
++) {
250 double di
= _V_ij(double(k
)/L
, double(i
)/L
);
251 double dj
= _V_ij(double(k
)/L
, double(j
)/L
);
252 ret
+= di
*(state
.parallel(k
, i
)?-1.0:1.0);
253 ret
+= dj
*(state
.parallel(k
, j
)?-1.0:1.0);
255 for (int k
=i
+1;k
<L
;k
++) {
256 double di
= _V_ij(double(k
)/L
, double(i
)/L
);
257 double dj
= _V_ij(double(k
)/L
, double(j
)/L
);
258 ret
+= di
*(state
.parallel(k
, i
)?-1.0:1.0);
259 ret
+= dj
*(state
.parallel(k
, j
)?-1.0:1.0);
261 return 2.0 * alpha
* ret
;
264 double ratio (const SpinChain
& s
, const SpinChain::Change
& c
) const {
265 const double exponent
= diff(s
, c
);
266 return _signRatio(c
)*exp(exponent
);
269 double diffHelper (const Helper
& h
, const SpinChain::Change
& c
) const {
270 const std::vector
<double>& V
= h
.V
;
271 const std::vector
<double>& v_ij
= h
.v_ij
;
272 const int L
= V
.size();
273 double d
= v_ij
[abs(c
.i
-c
.j
)];
274 // compensate double counting: i, j are surely antiparallel, so had a -1 sign
275 // they are counted once in v[i] and once in v[j]
276 // with the wrong sign
278 return -2.0* alpha
* (V
[c
.i
]+V
[c
.j
]+x
);
280 double ratioHelper (const Helper
& h
, const SpinChain::Change
& c
) const {
281 const double exponent
= diffHelper(h
, c
);
282 return _signRatio(c
)*exp(exponent
);
284 void applyChangeHelper (const SpinChain
& s
, Helper
& h
, const SpinChain::Change
& c
) const {
285 std::vector
<double>& V
= h
.V
;
286 std::vector
<double>& v_ij
= h
.v_ij
;
287 const int L
= V
.size();
288 const int i
= MAX(c
.i
, c
.j
);
289 const int j
= MIN(c
.i
, c
.j
);
292 for (int k
=0;k
<j
;k
++) {
293 double di
= v_ij
[i
-k
];
294 double dj
= v_ij
[j
-k
];
295 V
[k
] -= 2.0*di
*(s
.parallel(k
, i
)?-1.0:1.0);
296 V
[k
] -= 2.0*dj
*(s
.parallel(k
, j
)?-1.0:1.0);
298 for (int k
=j
+1;k
<i
;k
++) {
299 double di
= v_ij
[i
-k
];
300 double dj
= v_ij
[k
-j
];
301 V
[k
] -= 2.0*di
*(s
.parallel(k
, i
)?-1.0:1.0);
302 V
[k
] -= 2.0*dj
*(s
.parallel(k
, j
)?-1.0:1.0);
304 for (int k
=i
+1;k
<L
;k
++) {
305 double di
= v_ij
[k
-i
];
306 double dj
= v_ij
[k
-j
];
307 V
[k
] -= 2.0*di
*(s
.parallel(k
, i
)?-1.0:1.0);
308 V
[k
] -= 2.0*dj
*(s
.parallel(k
, j
)?-1.0:1.0);
310 double d
= v_ij
[i
-j
];
314 void computeHelper (const SpinChain
& s
, Helper
& h
) {
315 const int L
= s
.size();
316 std::vector
<double>& V
= h
.V
;
317 std::vector
<double>& v_ij
= h
.v_ij
;
318 if (V
.size()<L
) V
.resize(L
);
319 if (v_ij
.size()<L
) v_ij
.resize(L
);
320 for (int i
=0;i
<L
;i
++) {
321 v_ij
[i
] = _V_ij(double(i
)/L
, 0.0);
323 for (int i
=0;i
<L
;i
++) {
325 for (int j
=0;j
<i
;j
++) {
326 double d
= v_ij
[i
-j
];
327 x
+= d
*(s
.parallel(i
, j
)?1.0:-1.0);
329 for (int j
=i
+1;j
<L
;j
++) {
330 double d
= v_ij
[j
-i
];
331 x
+= d
*(s
.parallel(i
, j
)?1.0:-1.0);
343 double _H_i (const SpinChain
& s
, const VarProb
& p
, int i
) const {
346 const int L
= s
.size();
347 if (s
.parallel(i
, (i
+1)%L
)) {
350 SpinChain::Change flip
= { i
, (i
+1)%L
};
352 other
+= 0.5*J
*(p
.ratio(s
, flip
));
354 return (0.25*self
)+other
;
357 double _H_i (const SpinChain
& s
, const VarProb::Helper
& h
, const VarProb
& p
, int i
) const {
360 const int L
= h
.V
.size();
361 if (s
.parallel(i
, (i
+1)%L
)) {
364 SpinChain::Change flip
= { i
, (i
+1)%L
};
366 other
+= 0.5*J
*(p
.ratioHelper(h
, flip
));
368 return (0.25*self
)+other
;
371 Energy() : J(1.0), D(1.0) {}
372 Energy(double j
) : J(j
), D(1.0) {}
373 Energy(double j
, double d
) : J(j
), D(d
) {}
374 Energy(const Energy
&e
) : J(e
.J
), D(e
.D
) {}
376 double compute (const SpinChain
& s
, const VarProb
& p
) const {
378 const int L
= s
.size();
379 for (int i
=0;i
<L
;i
++) {
380 ret
+= _H_i (s
, p
, i
);
385 double compute (const SpinChain
& s
, const VarProb::Helper
& h
, const VarProb
& p
) const {
387 const int L
= s
.size();
388 for (int i
=0;i
<L
;i
++) {
389 ret
+= _H_i (s
, h
, p
, i
);
399 template <typename Probability
>
403 m_probability
.computeHelper(m_state
, m_helper
);
407 typedef typename
Probability::State State
;
408 typedef typename
State::Change Change
;
409 typedef typename
Probability::Helper Helper
;
411 QMC (const Probability
& p
, const State
& s
) : m_probability(p
), m_state(s
) {
415 const State
& state () const { return m_state
; }
416 void setState (const State
& s
) { m_state
= s
; _recompute(); }
417 const Probability
& probability () const { return m_probability
; }
418 void setProbability (const Probability
& p
) { m_probability
= p
; _recompute(); }
419 const Change
& lastChange () const { return last_change
; }
420 const Helper
& helper () const { return m_helper
; }
421 double weight () const { return 1.0; }
424 bool ret
= false; // whether it's accepted
425 Change trial
= m_state
.trialChange(); // description of a trial step
426 double pchange_h
= m_probability
.ratioHelper(m_helper
, trial
);
427 double a
= pchange_h
*pchange_h
; // pchange is in fact related to an amplitude
428 if (a
>(rand()/(RAND_MAX
+1.0))) {
430 m_state
.applyChange(trial
);
431 m_probability
.applyChangeHelper(m_state
, m_helper
, trial
); // XXX: leave after applyChange()
442 Probability m_probability
;
450 m_prob
.computeHelper(m_state
, m_helper
);
455 const double J
= m_energy
.J
;
456 const double D
= m_energy
.D
;
457 const int L
= m_state
.size();
460 int j
= 1; // 0 is reserved for no change
461 _G
.resize(L
+1, -1.0); // L spins can flip or no flip at all
462 _x
.resize(L
+1, -1); // L spins can flip or no flip at all
463 for (int i
=0;i
<L
;i
++) {
464 if (m_state
.parallel(i
, (i
+1)%L
)) {
467 Change c
= { i
, (i
+1)%L
};
468 const double g_i
= 0.5 * J
* m_prob
.ratioHelper(m_helper
, c
); // guiding function
469 e_L
+= g_i
; // e_L is the sum of all the green function column
470 _G
[j
] = -g_i
; // the energy due to the change
471 _x
[j
++] = i
; // the spin that is flipped
472 self
-= J
*D
; // the energy of the state
475 for (;j
<=L
;j
++) { // overwrite possible leftover values
482 e_L
= self
+e_L
; // e_L is now correct
484 //std::cout << e_L-m_energy.compute(m_state, m_helper, m_prob) << std::endl;
485 //std::cout << e_L-m_energy.compute(m_state, m_prob) << std::endl;
488 typedef SpinChain State
;
489 typedef /*typename*/ State::Change Change
;
490 typedef VarProb Probability
;
491 typedef /*typename*/ Probability::Helper Helper
;
493 explicit Walker(const Probability
&p
, const State
&s
, Energy e
= Energy()) : Lambda(-1.0), m_weight(1.0), m_energy(e
), m_state(s
), m_prob(p
) {
494 Lambda
= s
.size() * 0.25 * e
.J
* (0.1 + e
.D
); // surely higher than any value
498 const State
& state () const { return m_state
; }
499 void setState (const State
& s
) { m_state
= s
; _recompute(); }
500 const Probability
& probability () const { return m_prob
; }
501 void setProbability (const Probability
& p
) { m_prob
= p
; _recompute(); }
502 const Helper
& helper () const { return m_helper
; }
503 double weight () const { return m_weight
; }
504 double localEnergy () const { return m_localEnergy
; }
507 const int L
= m_state
.size();
508 const double b
= (Lambda
- m_localEnergy
); // will be the new weight
509 double r
= b
*rand()/(RAND_MAX
+1.0);
511 while (r
>_G
[j
] && _G
[j
]>0) {
512 //cout << j << " " << r << " " << G[j] << endl;
515 //if (_G[j]<0.0) throw std::string("WTF?!");
516 // now j contains the index of the spin that must be flipped
517 const int pos
= _x
[j
];
518 //std::cout << "stuck on " << j << " " << r << " " << _G[j] << " " << pos << std::endl;
519 //std::cout << pos << "\n"; // to check for translation invariance
524 const Change f
= { pos
, (pos
+1)%L
};
525 m_state
.applyChange(f
);
526 m_prob
.applyChangeHelper(m_state
, m_helper
, f
); // XXX: leave after applyChange()
535 double m_localEnergy
;
540 std::vector
<double> _G
; // green function for a flip
541 std::vector
<int> _x
; // which spin is flipped
549 int main (int argc
, char **argv
) {
550 bool print_state
= false;
557 SpinChain init
= SpinChain::neel(nspins
);
558 for (int i
=1;i
<argc
;i
++) {
559 if (argv
[i
][0]!='-') {
563 switch (argv
[i
][1]) {
567 filename
= argv
[++i
];
570 srand(atoi(argv
[++i
]));
573 niter
= atoi(argv
[++i
]);
576 alpha
= atof(argv
[++i
]);
579 nspins
= atoi(argv
[++i
]);
580 init
= SpinChain::neel(nspins
);
594 if (filename
!="") out
.open(filename
.c_str(), ofstream::out
);
595 //QMC<VarProb> mc(VarProb(alpha), init);
596 Walker
mc(VarProb(alpha
), init
);
599 bool accepted
= true; // first value is always accepted
603 out
<< "#!prepend " << alpha
<< "\n";
604 out
<< "# alpha = " << alpha
<< "\n";
605 out
<< "# format = '^([01]+)%s+(.*)$'\n";
607 std::string state_str
= init
.toString();
608 for (int i
=1;i
<=niter
;i
++) {
610 // e still contains the correct value
612 const SpinChain
& s
= mc
.state();
613 //e = en.compute(s, mc.helper(), mc.probability());
614 e
= mc
.localEnergy();
615 if (out
.is_open()) state_str
= s
.toString();
617 w
= std::log(mc
.weight());
620 out
<< state_str
<< " " << (e
/nspins
) << " " << w
<< "\n";
622 out
<< "* " << (e
/nspins
) << " " << w
<< "\n";
625 accepted
= mc
.step();