import java.util.*;
class PottsModel extends PottsModelBase {
private double currentEn;
private int nMeasurements;
private double currentM;
MersenneTwisterFast mersenneTwisterFast = new MersenneTwisterFast();
double[][] J;
public PottsModel(int N,double q) {
super(N,q);
init();
}
public void init() {
for(int i=0;i<N;i++) {
for(int j=0;j<N;j++) {
s[i][j]=-1;
}
}
J = (new Current(N)).J;
calcEnergy();
resetRunningAverage();
}
public void resetRunningAverage() {
M=computeMagnetization();
currentM = M;
nMeasurements = 1;
}
private double computeMagnetization() {
double m=0;
for(int i=0;i<N;i++) {
for(int j=0;j<N;j++) {
m+=s[i][j];
}
}
return m/(N*N);
}
private double calcEnergy() {
double en = 0;
for(int i=0;i<N;i++) {
int ir = (i+1)%N;
for(int j=0;j<N;j++) {
int jr = (j+1)%N;
en += -s[i][j]*(s[ir][j]+s[i][jr]);
en += bias*J[i][j]*s[i][j];
}
}
currentEn = en;
return en;
}
private double flipEnergy(int i,int j,boolean up) {
double dE;
int ir = (i+1)%N;
int il = i-1;
if(il<0) {
il += N;
}
int jr = (j+1)%N;
int jl = j-1;
if(jl<0) {
jl += N;
}
double nbSum = s[i][jr]+s[i][jl]+s[ir][j]+s[il][j];
if(up) {
dE = -nbSum/q + bias*J[i][j]/q;
} else {
dE = nbSum/q-bias*J[i][j]/q;
}
return dE;
}
private double flipEnergy2(int i,int j,double ds) {
double dE;
int ir = (i+1)%N;
int il = i-1;
if(il<0) {
il += N;
}
int jr = (j+1)%N;
int jl = j-1;
if(jl<0) {
jl += N;
}
double nbSum = s[i][jr]+s[i][jl]+s[ir][j]+s[il][j];
dE = -nbSum*ds + bias*J[i][j]*ds;
return dE;
}
private void update2(int i,int j) {
double oldS = s[i][j];
double dM;
double newS = 2*Math.random()-1;
double ds = newS-oldS;
double dE = flipEnergy2(i,j,ds);
double newEn = currentEn + dE;
double pnewOverp0 = Math.exp((currentEn-newEn)/T);
boolean accept = false;
if(pnewOverp0>=1) {
accept = true;
} else {
double r = Math.random();
if(r<pnewOverp0) {
accept = true;
}
}
//System.out.println("acept="+accept);
if(accept) {
s[i][j] = newS;
dM = ds/(N*N);
} else {
dM = 0;
}
double oldM = currentM;
currentM += dM;
M = (M*nMeasurements + currentM)/(nMeasurements + 1);
nMeasurements++;
}
private void update(int i,int j) {
double state = s[i][j];
boolean up;
double dM;
if(state<-.99) {
up = true;
} else if(state>.99) {
up = false;
} else {
double r = Math.random();
//double r = mersenneTwisterFast.nextDouble();
if(r>.5) {
up = true;
} else {
up=false;
}
}
double dE = flipEnergy(i,j,up);
//System.out.println("dE="+dE);
//System.out.println("dcurreEn="+currentEn);
double newEn = currentEn + dE;
double pnewOverp0 = Math.exp((currentEn-newEn)/T);
boolean accept = false;
if(pnewOverp0>=1) {
accept = true;
} else {
double r = Math.random();
//double r = mersenneTwisterFast.nextDouble();
if(r<pnewOverp0) {
accept = true;
}
}
//System.out.println("acept="+accept);
if(accept) {
if(up) {
s[i][j] += 1/q;
dM = (1/q)/(N*N);
} else {
s[i][j] -= 1/q;
dM = -(1/q)/(N*N);
}
} else {
dM = 0;
}
double oldM = currentM;
currentM += dM;
M = (M*nMeasurements + currentM)/(nMeasurements + 1);
nMeasurements++;
}
public void sweep() {
//System.out.println("m="+getMagnetization());
for(int i=0;i<N;i++) {
for(int j=0;j<N;j++) {
update(i,j);
}
}
}
}
class Current {
public int N;
public double[][] J;
public Current(int N) {
this.N=N;
J = new double[N][N];
setJ();
}
private void setJ() {
double x,y;
double rad=.3;
double radEye = .07;
double p=.7;
double peye = 0.0;
for(int i=0;i<N;i++) {
x = i/(N-1.);
for(int j=0;j<N;j++) {
y = j/(N-1.);
double r2 = (x-.5)*(x-.5)+(y-.5)*(y-.5);
double wrongRad = rad + p*(2*Math.random()-1)*rad*rad;
if(r2<wrongRad*wrongRad) {
J[i][j] = -1;
} else {
J[i][j] = 1;
}
r2 = (x-.4)*(x-.4)+(y-.6)*(y-.6) + peye*(2*Math.random()-1)*radEye*radEye;
if(r2<radEye*radEye) {
J[i][j] = 1;
}
r2 = (x-.6)*(x-.6)+(y-.6)*(y-.6)+ peye*(2*Math.random()-1)*radEye*radEye;
if(r2<radEye*radEye) {
J[i][j] = 1;
}
double rr = Math.random();
if(rr<p) {
rr = Math.random();
if(rr<.5) {
J[i][j] = -1;
} else {
J[i][j] = 1;
}
}
}
}
}
}