/* Do Metropolis/Swendsen-Wang/Sweeny/Gliozzi/Wolff dynamics D dimensions, general Q Potts model. Compute decorrelation time tau from the definition var(A_M) = tau var(A_1)/M, where A_M = (1/M) sum( A_i), A is energy Speed: 0.9 (700MHz Pentium 3), 0.6 (600MHz alpha), 0.4 (1.5G Athlon) * 10^-6 sec per SW spin flip 14 June 2002 by Wang Jian-Sheng, wangjs@cz3.nus.edu.sg */ #undef NDEBUG /* #define will turn off assertion code */ #undef MERSENNE_TWISTER /* random number, Mersenne or lcg */ #define NR_RANDOM_NUMBER /* #define use "Numerical Recipes" ran2 */ #include #include #include #include #include /* macros */ #define ZMAX 10 /* max Z, good upto D=5 */ #define LOOP 65536 /* number of ``runs'' */ #define N_WRITE 1 /* how often to write */ #define TK 16 /* buffer array size */ #define MMAX 65536 /* max M, must be 2^TK or larger */ #ifdef MERSENNE_TWISTER #define srand64 sgenrand #define drand64 genrand #endif /* globals */ int Algo; /* 0 metropolis, 1 SW, 2 sweeny, 3 gliozzi, 4 wolff */ int D; /* dimension */ int L; /* lattice linear size */ int N; /* total number of spins */ int Z; /* 2*D, coordination */ double Q; /* number of Potts states */ int MCTOT; /* total Monte Carlo sweeps */ int MCDIS; /* sweeps discarded in the beginning */ double Tr, P; /* Tc and Pc */ int *s; /* Potts spin, 0 to Q-1 */ int *bond; /* bond[D*i+d] at site i, direction d */ int *list; /* site i has label list[i] */ int Csize; /* Cluster size */ long double buff[MMAX]; /* circular buffer for energy */ long double sum[TK+1]; /* sum E_i */ long double ave[TK+1]; /* */ long double var[TK+1]; /* <(sum E_i/M)^2> */ /* function prototypes */ void init(void); void sweeny(void); int connect(int i, int j, int *bond); void sw(void); void metrop(int step); void flip(int i, int s0, int q); void wolff(int n); int e_change(int i, int q); long double energy(void); void variance(long double e); void neighbor(int i, int nn[ ]); double drand64(void); void srand64(unsigned long seed); int main(int argc, char **argv) { long double u, C_av, C_av_all, E_av, E_av_all, e, eng, eng2, engk, ckT; long double tau[TK+1], err[TK+1], err1; long double ave_all[TK+1], var_all[TK+1]; int mc, k, d, dt, loop, fac = 1; FILE *fin, *fout; fin = stdin; fout = stdout; if(argc >= 2) { fin = fopen(argv[1], "r"); assert(fin != NULL); } fprintf(stderr, "enter Algo (0 Metropolis, 1 SW, 2 Sweeny, 3 Gliozzi, 4 Wolff):\n"); fscanf(fin, "%d", &Algo); fprintf(stderr, "enter dimension D:\n"); fscanf(fin, "%d", &D); fprintf(stderr, "enter Potts states Q:\n"); fscanf(fin, "%lf", &Q); fprintf(stderr, "enter size L:\n"); fscanf(fin, "%d", &L); fprintf(stderr, "enter MCTOT:\n"); fscanf(fin, "%d", &MCTOT); fprintf(stderr, "enter MCDIS:\n"); fscanf(fin, "%d", &MCDIS); if(argc >= 2) { fclose(fin); } assert(Q>0.0 && D>0 && L>0); Z = 2 * D; assert(ZMAX >= Z); /* enlarge ZMAX if this assertion failed */ N = 1; for(d = 0; d < D; ++d) { /* N = L^D is total number of sites */ N *= L; } loop = 0; for(k = 0; k <= TK; ++k) { tau[k] = 0.0; err[k] = 0.0; ave_all[k] = 0.0; var_all[k] = 0.0; } C_av = 0.0; C_av_all = 0.0; E_av = 0.0; E_av_all = 0.0; init(); /* initialize s[], buff[], sum[], var[], A_ex etc */ Loop: for(mc = 1; mc <= MCTOT; ++mc) { if(Algo == 2 || Algo == 3) { sweeny(); } else if (Algo == 1) { sw(); } else if (Algo == 4) { Csize = 0; wolff(1); C_av += Csize; /* average cluster size */ } else { metrop(N); } e = energy(); variance(e); if(mc == MCDIS) { for(k = 0; k <= TK; ++k) { ave[k] = 0.0; var[k] = 0.0; /* discard accumulated numbers */ } /* for mc = 1, 2, ..., MCDIS */ E_av = 0.0; C_av = 0.0; } if(mc > MCDIS) { /* do statistics */ E_av += e; } } ++loop; dt = 1; eng = E_av/(MCTOT-MCDIS); for(k = 0; k <= TK; ++k) { engk = ave[k]/(MCTOT-MCDIS); u = dt * (var[k]/(MCTOT-MCDIS)-engk*engk)/(var[0]/(MCTOT-MCDIS)-eng*eng); tau[k] += u; /* binned value for error only */ err[k] += u*u; /* err[] is actually */ dt *= 2; ave_all[k] += ave[k]; /* sum up the loop runs */ var_all[k] += var[k]; } E_av_all += E_av; C_av_all += C_av; if(loop % (fac*N_WRITE) == 0) { fac *= 2; if(argc == 3) { /* open file to append only when write */ fout = fopen(argv[2], "a"); assert(fout != NULL); } switch (Algo) { case 0: fprintf(fout, "Single spin flip - "); break; case 1: fprintf(fout, "SW dynamics - "); break; case 2: fprintf(fout, "Sweeny-heat-bath dynamics - "); break; case 3: fprintf(fout, "Gliozzi dynamics - "); break; case 4: fprintf(fout, "Wolff dynamics - "); break; default: fprintf(stderr, "unknown algo!\n"); exit(1); } fprintf(fout, "%dD Q=%g Potts, L=%d, %d MCS used, %d discard, %d runs\n", D, Q, L, MCTOT-MCDIS, MCDIS, loop); if(Algo == 4) { fprintf(fout, " = %Lg\n", C_av_all/(MCTOT-MCDIS)/loop); } eng = E_av_all/(MCTOT-MCDIS)/loop; if(Algo == 2 || Algo == 3) { ckT = ( (var_all[0]/(MCTOT-MCDIS)/loop-eng*eng) + eng*(1-P)/P)/N; } else { ckT = (var_all[0]/(MCTOT-MCDIS)/loop-eng*eng)/N; } fprintf(fout, " = %Lg cT^2 = %Lg\n", eng/N, ckT); fprintf(fout, " M tau err \n"); dt = 1; for(k = 0; k <= TK; ++k) { engk = ave_all[k]/(MCTOT-MCDIS)/loop; eng2 = var_all[k]/(MCTOT-MCDIS)/loop; u = dt * (eng2-engk*engk) / (var_all[0]/(MCTOT-MCDIS)/loop-eng*eng); fprintf(fout, "%5d %Lg", dt, u); if (loop > 1) { err1 = sqrtl((err[k]/loop-(tau[k]/loop)*(tau[k]/loop))/(loop-1)); fprintf(fout, " +/- %Lg ", err1); } else { fprintf(fout, " +/- ? "); } fprintf(fout, " %.15Lg %.15Lg\n", eng2/N/N, engk/N); dt *= 2; } fflush(fout); if(argc == 3) { fclose(fout); } } if(loop < LOOP) goto Loop; return 0; } void sweeny(void) /* Sweeny/Gliozzi algorithm */ { int mc, i, j, k, d; int nn[ZMAX]; double r; for(mc = 0; mc < D*N; ++mc) { /* go over each bonds on average */ k = D * N * drand64(); /* pick a bond at random */ i = k / D; /* site number */ d = k % D; /* direction */ assert(i >= 0 && i < N); neighbor(i, nn); j = nn[2*d]; r = drand64(); if(Algo == 2) { /* Sweeny - PRB 27, 4445 */ bond[k] = 0; if (connect(i,j, bond)) { bond[k] = r < P; } else { bond[k] = r < P/( (1-P)*Q+P ); } } else if (Algo == 3) { /* Gliozzi, cond-mat/0201285 */ if( bond[k] || connect(i,j,bond) ) { bond[k] = r < P; } else { bond[k] = r < P/Q; } } } } long long int *mark; int *stack; /* Decide if site i and j are on the same cluster */ int connect(int i, int j, int *bond) { int pt, jj, k, find, nn[ZMAX], b[ZMAX]; static long long int inc = 0; if(inc == 0) { for(k = 0; k < N; ++k) { assert(mark[k] == 0); } } ++inc; stack[0] = i; mark[i] = inc; pt = 1; /* points to the 1st available slot */ find = 0; while(pt > 0) { assert(pt < N); k = stack[--pt]; /* pop one from bottom */ neighbor(k, nn); for(jj = 0; jj < D; ++jj) { b[2*jj] = bond[D*k + jj]; b[2*jj+1] = bond[D*nn[2*jj+1]+jj]; } for(jj = 0; jj < Z; ++jj) { if (b[jj] && mark[nn[jj]] != inc) { mark[nn[jj]] = inc; if (j == nn[jj]) { find = 1; goto Stop; } else { stack[pt++] = nn[jj]; } } } } Stop: return find; } void sw(void) /* Swendsen-Wang algorithm, perform one Swendsen-Wang step */ { int i, ip, j, cnt, inc, a, b, min, max; int r, p, q; for(i = 0; i < N; ++i) { /* clear or initialize list */ list[i] = i; /* initially each site is a cluster by itself */ } /* later, site i has the same label as site list[i] (recursively) */ for(i = 0; i < N; ++i) { /* set the bond with prob P */ cnt = 0; r = i; p = 1 - L; q = 1; Repeat: /* D times */ ip = (r + 1) % L == 0 ? i + p : i + q; if(s[i] == s[ip] && drand64() < P) { /* implement Hoshen-Kopelman */ a = list[i]; while (a > list[a]) { /* run through until a == list[a] */ a = list[a]; } b = list[ip]; while (b > list[b]) { b = list[b]; } if (a > b) { /* find min and max of two labels */ min = b; max = a; } else { min = a; max = b; } list[max] = min; list[i] = min; } ++cnt; if(cnt < D) { r = r/L; p *= L; q *= L; goto Repeat; } } inc = 0; /* last sweep to make list pointing to the final label */ for(i = 0; i < N; ++i) { if(i == list[i]) { s[i] = inc; /* spin value over-written by label */ ++inc; /* a new cluster find */ } else { j = list[i]; while (j > list[j]) { j = list[j]; } assert(j < i); s[i] = s[j]; /* one of old cluster */ } } assert(inc <= N); for(j = 0; j < inc; ++j) { list[j] = Q*drand64(); /* new spin for each cluster */ } for(i = 0; i < N; ++i) { assert(s[i] < inc); s[i] = list[s[i]]; /* old s[i] is cluster number */ } } /* What is the energy change if site i is changed to state q from s[i]. */ int e_change(int i, int q) { int nn[ZMAX]; int e, si, sj, j; neighbor(i, nn); si = s[i]; e = 0; for(j = 0; j < Z; ++j) { sj = s[nn[j]]; e += (si == sj) - (q == sj); } return e; } /* do Metropolis canonical Monte Carlo, temperarure Tr, size N are globals. */ void metrop(int step) { int mc, i, q, de; for(mc = 0; mc < step; ++mc) { i = N * drand64(); q = (Q-1) * drand64(); if(q >= s[i]) { ++q; } de = e_change(i, q); if (de <= 0 || drand64() < exp(-de/Tr)) { /* Metropolis rate */ s[i] = q; } } } /* This function wolff() performs n cluster flips, equivalent to few Monte Carlo steps in standard single-spin-flip algorithms. It picks a seed site at random and calls the flip function to generate one cluster. N is total number of spin (a global). */ void wolff(int n) { int i, k, q; assert(n == 1); for(k = 0; k < n; ++k) { i = drand64() * (double) N; q = (Q-1) * drand64(); /* choose a new spin value */ if(q >= s[i]) { ++q; } flip(i, s[i], q); } } /* Perform a Wolff single cluster flip. s[], P, and Z are passed globally. The first argument i of flip function is the site to be flipped, the second argument is the spin of the cluster before flipping. q is the spin value to flip into. Recursive version may cause stack overflow. */ void flip(int i, int s0, int q) { int j, nn[ZMAX]; ++Csize; /* count the size of cluster */ s[i] = q; /* flip the spin immediately */ neighbor(i, nn); /* find nearest neighbor of i */ for(j = 0; j < Z; ++j) /* flip the neighbor if ... */ if(s0 == s[nn[j]] && drand64() < P) flip(nn[j], s0, q); } /* Neighbor returns in the array nn[ ] the neighbor sites of i. The sites are labelled sequentially, starting from 0. It works for any hypercubic lattice. Z (=2*D) is the coordination number, L is linear size, all passed from globals. */ void neighbor(int i, int nn[ ]) { int j, r, p, q; r = i; p = 1 - L; q = 1; for(j = 0; j < Z; j += 2) { nn[j] = (r + 1) % L == 0 ? i + p : i + q; nn[j+1] = r % L == 0 ? i - p : i - q; r = r/L; p *= L; q *= L; } } /* This function computes the energy of the current configuration s[]. The energy is defined as E = - sum ( delta (s[i], s[j]), */ long double energy(void) { int b, k; int i, ip, ie, si, j; int r, p, q; if(Algo == 2 || Algo == 3) { b = 0; for(k = 0; k < D*N; ++k) { b += bond[k]; } return -(long double) b/P; } else { ie = 0; for(i = 0; i < N; ++i) { si = s[i]; assert(si >= 0 && si < Q); r = i; p = 1 - L; q = 1; for(j = 0; j < D; ++j) { ip = (r + 1) % L == 0 ? i + p : i + q; ie += (si==s[ip]); r = r/L; p *= L; q *= L; } } return (long double) -ie; } } void variance(long double e) { int dt, k, t; long double del; static int tpt = 0; /* current value is e, most recent value was at tpt (before increment) */ dt = 1; /* how many terms in partial sum */ tpt = (tpt+1) % MMAX; for(k = 0; k <= TK; ++k) { /* from dt = 1 to 2^TK */ assert(dt <= MMAX); t = (tpt - dt + MMAX) % MMAX; /* subtract dt, wrap around */ sum[k] += (e - buff[t]); /* sum_{t=t0+1}^{t0+dt} e_t for all t0 */ dt *= 2; } buff[tpt] = e; dt = 1; for(k = 0; k <= TK; ++k) { /* k is lg2 dt */ del = sum[k]/dt; /* i.e. delta A */ ave[k] += del; var[k] += del*del; dt *= 2; } } /* 2D Ising exact energy and specific heat per spin */ #if L==4 #define EN_EXACT -1.56562378763832 #define CA_EXACT 0.78326682592891 #elif L==8 #define EN_EXACT -1.491589107439706 #define CA_EXACT 1.145559239894408 #elif L==16 #define EN_EXACT -1.453064852813477 #define CA_EXACT 1.498704959400026 #elif L==32 #define EN_EXACT -1.433658466146248 #define CA_EXACT 1.846767590039559 #elif L==64 #define EN_EXACT -1.423938389833011 #define CA_EXACT 2.192211393140571 #elif L==128 #define EN_EXACT -1.419076272084985 #define CA_EXACT 2.536331335108602 #elif L==256 #define EN_EXACT -1.416644954196832 #define CA_EXACT 2.879786255202499 #elif L==512 #define EN_EXACT -1.4154292629050 #define CA_EXACT 3.22290795 #elif L==1024 #define EN_EXACT -1.41482141321652494 #define CA_EXACT 3.565862873717 #endif /* Do initialization of spins, coupling, mat[], etc */ void init(void) { int i, k; if(Algo == 1) { list = (int *) malloc(N*sizeof(int)); assert(list != NULL); } if(Algo == 2 || Algo == 3) { bond = (int *) malloc(D*N*sizeof(int)); mark = (long long int *) calloc(N, sizeof(long long int)); stack = (int *) malloc(N*sizeof(int)); assert(bond != NULL); assert(mark != NULL); assert(stack != NULL); } else { /* for Algo = 0, 1, 4 */ s = (int *) malloc(N*sizeof(int)); assert(s != NULL); } if(D == 2) { Tr = (1.0/(log(sqrt((double)Q)+1.0))); P = (1.0-1.0/(sqrt((double)Q)+1.0)); } else if (D == 3 && Q == 2) { Tr = 1.0/(0.221657*2.0); /* using Potts scaled */ P = 1.0 - exp(-1.0/Tr); } else { Tr = D/2.0; P = 1.0 - exp(-1.0/Tr); } srand64(time(NULL)); if(Algo == 2 || Algo == 3) { for(k = 0; k < D*N; ++k) { bond[k] = (drand64() < P); } } else { for(i = 0; i < N; ++i) { s[i] = Q * drand64(); } } for(i = 0; i < MMAX; ++i) { buff[i] = 0; } for(k = 0; k <= TK; ++k) { sum[k] = 0.0; ave[k] = 0.0; var[k] = 0.0; } } #ifdef NR_RANDOM_NUMBER /* then use "Numerical Recipes" random number ran2 */ #define IM1 2147483563 #define IM2 2147483399 #define AM (1.0/IM1) #define IMM1 (IM1-1) #define IA1 40014 #define IA2 40692 #define IQ1 53668 #define IQ2 52774 #define IR1 12211 #define IR2 3791 #define NTAB 32 #define NDIV (1+IMM1/NTAB) #define EPS 1.2e-13 #define RNMX (1.0-EPS) static long int idum = 1; double drand64(void) /* this is "Numerical Recipes" ran2(idum) */ { int j; long k; static long idum2=123456789; static long iy=0; static long iv[NTAB]; double temp; if( idum <= 0 ) { if( ((-1)*idum ) < 1 ) idum = 1; else idum = (-1)*idum; idum2 = idum; for( j = NTAB+7 ; j>=0 ; j-- ) { k = idum/IQ1; idum = IA1 * (idum-k*IQ1)- k*IR1; if( idum < 0 ) idum += IM1; if( j< NTAB ) iv[ j ] = idum; } iy = iv[0]; } k = idum/IQ1; idum = IA1 * (idum-k*IQ1)- k*IR1; if( idum < 0 ) idum += IM1; k = idum2/IQ2; idum2 = IA2 * (idum2-k*IQ2)- k*IR2; if( idum2 < 0 ) idum2 += IM2; j = iy/NDIV; iy = iv[ j ]-idum2; iv[ j ] = idum; if( iy < 1 ) iy += IMM1; if( (temp = AM*iy ) > RNMX ) return RNMX; else return temp; } void srand64(unsigned long seed) { assert(sizeof(long int) == 4); assert(sizeof(long long int) == 8); assert(sizeof(long double) > sizeof(double)); idum = -seed; if(idum > 0) idum = - idum; drand64(); /* call with negative idum to initialize */ printf("NR ran2, initial seed x = %d\n", (int) seed); } #else #ifndef MERSENNE_TWISTER /* 64-bit random number generator */ static unsigned long long int x = 1; double drand64(void) { x = 6364136223846793005ll * x + (long long int) 1; return (double) x * 5.4210108624275218e-20; } void srand64(unsigned long seed) { assert(sizeof(long long int) == 8); x = seed; printf("drand 64-bit, initial seed x = %d\n", (int) x); } #endif #endif