q = 4 # theta discreteness
N = 64
n_dims = (N, N)
kB = 5.0
J = -1.0
spins = CircularArray(sample(0:q-1, Weights(ones(q)/q), n_dims))
# neighbors
locs = eachindex(spins)
basis_vec = eachcol(diagm(ones(Int8,length(n_dims)))) # general dimensions
neighbors = map(_n->CartesianIndex(_n...), [basis_vec; -basis_vec])
MAXITER = 100_000
STEP = MAXITER ÷ 1000
time = LinRange(0, 1, MAXITER)
βmax = 50.0
β = @. βmax * ((1 + exp(-2*(time-1/2)*10))^-1 - 1/2) #@. βmin - 2βmin*time
temp = @. 1.0 / β
spin_vec = zeros(size(spins)..., MAXITER)
for k in 1:MAXITER
spin_vec[:,:,k] = spins.data # save state
i = rand(locs) # choose random spin location
si = spins[i]
nearest = i .+ neighbors
energy = mapreduce(sj -> ham(J, q, si, sj), +, spins[nearest]) # sum
si_new = spin_flip(si, q)
energy_new = mapreduce(sj -> ham(J, q, si_new, sj), +, spins[nearest]) # sum
dE = energy_new - energy
if dE < 0.0
spins[i] = si_new
elseif rand() < exp(-dE * β[k])
spins[i] = si_new
end
end