mirror of
https://github.com/ImperialCollegeLondon/ReCoDE_MCMCFF.git
synced 2025-06-26 08:51:16 +02:00
112 lines
3.5 KiB
Python
112 lines
3.5 KiB
Python
"""ising_model provides functions specific to the 2D ising model such as computing its energy and generating special states.
|
|
|
|
States are represented by NxM numpy arrays with values +/-1. These functions are intended to be used with a markov chain monte carlo routine to sample from the thermal ensemble of the 2d Ising model.
|
|
|
|
"""
|
|
|
|
import numpy as np
|
|
from numba import jit
|
|
from matplotlib import pyplot as plt
|
|
|
|
|
|
def show_state(state, ax=None):
|
|
"Plot an Ising state to axis or make a new figure to plot to"
|
|
if ax is None:
|
|
f, ax = plt.subplots()
|
|
ax.matshow(state, cmap="Greys", vmin=-1, vmax=1)
|
|
ax.set(xticks=[], yticks=[])
|
|
|
|
|
|
def all_up_state(N):
|
|
"The NxN all up state of the Ising model."
|
|
return np.ones([N, N])
|
|
|
|
|
|
def all_down_state(N):
|
|
"The NxN all down state of the Ising model."
|
|
return -np.ones([N, N])
|
|
|
|
|
|
def random_state(N, magnetisation=0.5, rng=np.random.default_rng()):
|
|
r"""Return the a random NxN state of the Ising model.
|
|
|
|
Parameters
|
|
----------
|
|
N : int
|
|
The returned state will have shape (N,N)
|
|
magnetisation : int, default = 0.5
|
|
What proportion of the values will be +1
|
|
rng : numpy.random._generator.Generator, default = np.random.default_rng()
|
|
Optionally pass in the rng to be used, useful to get reproducable answers.
|
|
|
|
Returns
|
|
-------
|
|
np.array
|
|
A random NxN state.
|
|
"""
|
|
return np.random.choice([+1, -1], size=(N, N), p=magnetisation)
|
|
|
|
|
|
# nopython=True instructs numba that we want all use of the python interpreter to removed from this function
|
|
# numba will throw and error if it cannot achieve this.
|
|
# nogil = True says that numba can release python's Global Interpreter Lock, read more about that here: https://numba.pydata.org/numba-doc/latest/user/jit.html#nogil
|
|
@jit(nopython=True, nogil=True)
|
|
def energy(state):
|
|
r"""Compute the energy of a state of the Ising Model with open boundary conditions.
|
|
|
|
Parameters
|
|
----------
|
|
state : array_like
|
|
A NxM array containing only the values +1 and -1
|
|
Returns
|
|
-------
|
|
float64
|
|
The interaction energy per site.
|
|
"""
|
|
E = 0
|
|
N, M = state.shape
|
|
for i in range(N):
|
|
for j in range(M):
|
|
# handle the north and south neighbours
|
|
if 0 <= (i + 1) < N:
|
|
E -= state[i, j] * state[i + 1, j]
|
|
|
|
# handle the east and west neighbours
|
|
if 0 <= (j + 1) < M:
|
|
E -= state[i, j] * state[i, j + 1]
|
|
|
|
return 2 * E
|
|
|
|
|
|
def energy_numpy(state):
|
|
r"""Compute the energy of a state of the Ising Model with open boundary conditions.
|
|
|
|
An alternate implementation of `energy` using Numpy array operations, used for comparison and correctness checks.
|
|
|
|
Parameters
|
|
----------
|
|
state : array_like ()
|
|
A 2D array containing only the values +1 and -1
|
|
Returns
|
|
-------
|
|
float64
|
|
The interaction energy per site.
|
|
"""
|
|
E = -np.sum(state[:-1, :] * state[1:, :]) - np.sum(state[:, :-1] * state[:, 1:])
|
|
return 2 * E
|
|
|
|
|
|
@jit(nopython=True, nogil=True)
|
|
def energy_difference(state, site):
|
|
"The change in energy if we flipped site of state"
|
|
# loop over the four neighbours of the site, skipping if the site is near an edge
|
|
N, M = state.shape
|
|
i, j = site
|
|
h = 0
|
|
for di, dj in [[-1, 0], [1, 0], [0, -1], [0, 1]]: # loop over N,E,S,W neighbours
|
|
if (0 <= (i + di) < N) and (
|
|
0 <= (j + dj) < M
|
|
): # ignore neighbours not in the NxN grid
|
|
h += state[i + di, j + dj]
|
|
return 4 * state[i, j] * h
|