Python Ising Model Plotting - python

I should probably start of with saying I have no idea how to code and don't consider myself even a beginner when it comes to coding. That being said I would really appreciate some help with getting started with some code. As the title suggests I have to code what is known as the Ising model. The premise of the model is:
E= -Σ(hs(i)) - Σ(Js(i)*s(j))
this will follow what i believe is the Monte Carlo simulation. so for each configuration of {s(i)} there is a probability e^(-ßE{s(i)})
We start with a random spin to yield potential {s(i)}
If E(1)>E(0) we flip the sign
If E(1) < E(0), then you draw a random number and compare to e^(ß∆E)
if the number , say x is:
x< e^(ß∆E) then flip
x > e^(ß∆E) do nothing then {s(i)}={s(0)}
I hope that is enough info, but I did pickup some code which I think is relevant
import numpy as np
import random
def init_spin_array(rows, cols):
return np.ones((rows, cols))
def find_neighbors(spin_array, lattice, x, y):
left = (x, y - 1)
right = (x, (y + 1) % lattice)
top = (x - 1, y)
bottom = ((x + 1) % lattice, y)
return [spin_array[left[0], left[1]],
spin_array[right[0], right[1]],
spin_array[top[0], top[1]],
spin_array[bottom[0], bottom[1]]]
def energy(spin_array, lattice, x ,y):
return 2 * spin_array[x, y] * sum(find_neighbors(spin_array, lattice, x, y))
def main():
RELAX_SWEEPS = 50
lattice = eval(input("Enter lattice size: "))
sweeps = eval(input("Enter the number of Monte Carlo Sweeps: "))
for temperature in np.arange(0.1, 5.0, 0.1):
spin_array = init_spin_array(lattice, lattice)
# the Monte Carlo follows below
mag = np.zeros(sweeps + RELAX_SWEEPS)
for sweep in range(sweeps + RELAX_SWEEPS):
for i in range(lattice):
for j in range(lattice):
e = energy(spin_array, lattice, i, j)
if e <= 0:
spin_array[i, j] *= -1
elif np.exp((-1.0 * e)/temperature) > random.random():
spin_array[i, j] *= -1
mag[sweep] = abs(sum(sum(spin_array))) / (lattice ** 2)
print(temperature, sum(mag[RELAX_SWEEPS:]) / sweeps)
main()
All i need is to plot this info into a M vs T plot, and somehow change the code to allow three parameter h,J,T to be varied, as in if I hold T at a certain #, what is h vs. J plot look like. Please any help would be immensely appreciated.

Related

how to implement least square polynomial with no built in methods using python?

currently running into a problem solving this.
The objective of the exercise given is to find a polynom of certian degree (the degree is given) from a dataset of points (that can be noist) and to best fit it using least sqaure method.
I don't understand the steps that lead to solving the linear equations?
what are the steps or should anyone provide such a python program that lead to the matrix that I put as an argument in my decomposition program?
Note:I have a python program for cubic splines ,LU decomposition/Guassian decomposition.
Thanks.
I tried to apply guassin / LU decomposition straight away on the dataset but I understand there are more steps to the solution...
I donwt understand how cubic splines add to the mix either..
Edit:
guassian elimintaion :
import numpy as np
import math
def swapRows(v,i,j):
if len(v.shape) == 1:
v[i],v[j] = v[j],v[i]
else:
v[[i,j],:] = v[[j,i],:]
def swapCols(v,i,j):
v[:,[i,j]] = v[:,[j,i]]
def gaussPivot(a,b,tol=1.0e-12):
n = len(b)
# Set up scale factors
s = np.zeros(n)
for i in range(n):
s[i] = max(np.abs(a[i,:]))
for k in range(0,n-1):
# Row interchange, if needed
p = np.argmax(np.abs(a[k:n,k])/s[k:n]) + k
if abs(a[p,k]) < tol: error.err('Matrix is singular')
if p != k:
swapRows(b,k,p)
swapRows(s,k,p)
swapRows(a,k,p)
# Elimination
for i in range(k+1,n):
if a[i,k] != 0.0:
lam = a[i,k]/a[k,k]
a[i,k+1:n] = a[i,k+1:n] - lam*a[k,k+1:n]
b[i] = b[i] - lam*b[k]
if abs(a[n-1,n-1]) < tol: error.err('Matrix is singular')
# Back substitution
b[n-1] = b[n-1]/a[n-1,n-1]
for k in range(n-2,-1,-1):
b[k] = (b[k] - np.dot(a[k,k+1:n],b[k+1:n]))/a[k,k]
return b
def polyFit(xData,yData,m):
a = np.zeros((m+1,m+1))
b = np.zeros(m+1)
s = np.zeros(2*m+1)
for i in range(len(xData)):
temp = yData[i]
for j in range(m+1):
b[j] = b[j] + temp
temp = temp*xData[i]
temp = 1.0
for j in range(2*m+1):
s[j] = s[j] + temp
temp = temp*xData[i]
for i in range(m+1):
for j in range(m+1):
a[i,j] = s[i+j]
return gaussPivot(a,b)
degree = 10 # can be any degree
polyFit(xData,yData,degree)
I was under the impression the code above gets a dataset of points and a degree. The output should be coeefients of a polynom that fits those points but I have a grader that was provided by my proffesor , and after checking the grading the polynom that returns has a lrage error.
After that I tried the following LU decomposition instead:
import numpy as np
def swapRows(v,i,j):
if len(v.shape) == 1:
v[i],v[j] = v[j],v[i]
else:
v[[i,j],:] = v[[j,i],:]
def swapCols(v,i,j):
v[:,[i,j]] = v[:,[j,i]]
def LUdecomp(a,tol=1.0e-9):
n = len(a)
seq = np.array(range(n))
# Set up scale factors
s = np.zeros((n))
for i in range(n):
s[i] = max(abs(a[i,:]))
for k in range(0,n-1):
# Row interchange, if needed
p = np.argmax(np.abs(a[k:n,k])/s[k:n]) + k
if abs(a[p,k]) < tol: error.err('Matrix is singular')
if p != k:
swapRows(s,k,p)
swapRows(a,k,p)
swapRows(seq,k,p)
# Elimination
for i in range(k+1,n):
if a[i,k] != 0.0:
lam = a[i,k]/a[k,k]
a[i,k+1:n] = a[i,k+1:n] - lam*a[k,k+1:n]
a[i,k] = lam
return a,seq
def LUsolve(a,b,seq):
n = len(a)
# Rearrange constant vector; store it in [x]
x = b.copy()
for i in range(n):
x[i] = b[seq[i]]
# Solution
for k in range(1,n):
x[k] = x[k] - np.dot(a[k,0:k],x[0:k])
x[n-1] = x[n-1]/a[n-1,n-1]
for k in range(n-2,-1,-1):
x[k] = (x[k] - np.dot(a[k,k+1:n],x[k+1:n]))/a[k,k]
return x
the results were a bit better but nowhere near what it should be
Edit 2:
I tried the chebyshev method suggested in the comments and came up with:
import numpy as np
def chebyshev_transform(x, n):
"""
Transforms x-coordinates to Chebyshev coordinates
"""
return np.cos(n * np.arccos(x))
def chebyshev_design_matrix(x, n):
"""
Constructs the Chebyshev design matrix
"""
x_cheb = chebyshev_transform(x, n)
T = np.zeros((len(x), n+1))
T[:,0] = 1
T[:,1] = x_cheb
for i in range(2, n+1):
T[:,i] = 2 * x_cheb * T[:,i-1] - T[:,i-2]
return T
degree =10
f = lambda x: np.cos(X)
xdata = np.linspace(-1,1,num=100)
ydata = np.array([f(i) for i in xdata])
M = chebyshev_design_matrix(xdata,degree)
D_x ,D_y = np.linalg.qr(M)
D_x, seq = LUdecomp(D_x)
A = LUsolve(D_x,D_y,seq)
I can't use linalg.qr in my program , it was just for checking how it works.In addition , I didn't get the 'slow way' of the formula that were in the comment.
The program cant get an x point that is not between -1 and 1 , is there any way around it , any normalizition?
Thanks a lot.
Hints:
You are probably asked for an unsophisticated method. If the degree of the polynomial remains low, you can use the straightforward approach below. For the sake of the explanation, I'll use a cubic model.
Assume that you want to fit your data to this polynomial, by observing that it seems to follow a cubic behavior:
ax³ + bx² + cx + d ~ y
[All x and y should be understood with an index i which is omitted for notational convenience.]
If there are more than four data points, you get an overdetermined system of equations, usually with no solution. The trick is to consider the error on the individual equations, e = ax³ + bx² + cx + d - y, and to minimize the total error. As the error is a signed number, negative errors would make minimization impossible. Instead, we minimize the sum of squared errors. (The sum of absolute errors is another option but it unfortunately leads to a much harder problem.)
Min(a, b, c, d) Σ(ax³ + bx² + cx + d - y)²
As the unknown parameters are unconstrained, it suffices to look for a stationary point, i.e. cancel the gradient of the total error. By differentiation on the unknowns a, b, c and d, we obtain
2Σ(ax³x³ + bx²x³ + cxx³ + dx³ - yx³) = 0
2Σ(ax³x² + bx²x² + cxx² + dx² - yx²) = 0
2Σ(ax³x + bx²x + cxx + dx - yx ) = 0
2Σ(ax³ + bx² + cx + d - y ) = 0
As you can recognize, this is a square linear system of equations.

Simulated annealing converges to wrong global minima

I implemented simulated annealing to find global minima of a given function using
https://perso.crans.org/besson/publis/notebooks/Simulated_annealing_in_Python.html
but although the temperature is high at first and then decreases slowly (cause of steps), but it some times gives me the wrong answer.(local minima)
I have to add I tried to solve the problem using random start hill climbing and below is the list of local minimums in given interval:
x = 0.55 0.75 0.95 1.15 1.35 1.54 1.74 1.94 2.14 2.34 2.5
y = -0.23 -0.37 -0.47 -0.57 -0.66 -0.68 -0.55 -0.16 0.65 2.10 5.06
and optimize.basinhopping()prove that the global minima is (1.54, -.68)
here is the code:
import math
import numpy as np
import numpy.random as rn
import matplotlib.pyplot as plt # to plot
from scipy import optimize # to compare
import seaborn as sns
def annealing(random_start, func, func_interval, random_neighbour, acceptance, temperature, maxsteps=1000, debug=True):
""" Optimize the black-box function 'func' with the simulated annealing algorithm."""
x = random_start(func_interval)
y = func(x)
x_list, y_list = [x], [y]
for step in range(maxsteps):
fraction = step / float(maxsteps)
T = temperature(fraction)
new_x = random_neighbour(x, func_interval, fraction)
new_y = func(new_x)
if debug: print("Step #{:>2}/{:>2} : T = {:>4.3g}, x = {:>4.3g}, y = {:>4.3g}, new_x = {:>4.3g}, new_y = {:>4.3g} ...".format(step, maxsteps, T, x, y, new_x, new_y))
if acceptance_probability(y, new_y, T) > rn.random():
x, y = new_x, new_y
x_list.append(x)
y_list.append(y)
# print(" ==> Accept it!")
# else:
# print(" ==> Reject it...")
return x, func(x), x_list, y_list
def clip(x, func_interval):
""" Force x to be in the interval."""
a, b = func_interval
return max(min(x, b), a)
def random_start(func_interval):
""" Random point in the interval."""
a, b = func_interval
return a + (b - a) * rn.random_sample()
def random_neighbour(x, func_interval, fraction=1):
"""Move a little bit x, from the left or the right."""
amplitude = (max(func_interval) - min(func_interval)) * fraction / 10
delta = (-amplitude/2.) + amplitude * rn.random_sample()
return clip(x + delta, func_interval)
def acceptance_probability(y, new_y, temperature):
if new_y < y:
# print(" - Acceptance probabilty = 1 as new_y = {} < y = {}...".format(new_y, y))
return 1
else:
p = np.exp(- (new_y - y) / temperature)
# print(" - Acceptance probabilty = {:.3g}...".format(p))
return p
def temperature(fraction):
""" Example of temperature dicreasing as the process goes on."""
return max(0.01, min(1, 1 - fraction))
def see_annealing(x, y, x_list, y_list):
sns.set(context="talk", style="darkgrid", palette="hls", font="sans-serif", font_scale=1.05)
xs = np.linspace(func_interval[0], func_interval[1], 1000) # Get 1000 evenly spaced numbers between .5 and 2.5
plt.plot(xs, np.vectorize(func)(xs))
plt.scatter(x_list, y_list, c="b")
plt.scatter(x, y, c="r")
plt.title("Simulated annealing")
plt.show()
if __name__ == '__main__':
func = lambda x: math.sin(10 * math.pi * x) / 2 * x + (x - 1) ** 4
func_interval = (.5, 2.5)
x, y, x_list, y_list = annealing(random_start, func, func_interval, random_neighbour, acceptance_probability, temperature, maxsteps=1000, debug=False)
see_annealing(x, y, x_list, y_list)
print(x, y)
print(optimize.basinhopping(lambda x: math.sin(10*math.pi*x)/2*x + (x-1)**4, [random_start(func_interval)]))
But What's wrong?
Edit:
#user3184950 you are right, the algorithm is working better now but here is the pseudo code from AIMA third edition
next is just a random selected successor of current.
In addition I wrote a note from my AI course that simulated annealing is guaranteed to converge to the global maximum if we start T high and decrease it slowly enough.(I mean my professor didn't say anything about the "next point"
or I missed it somehow or maybe it just doesn't matter).
By the way, I was thinking the problem is with the chance of taking "next point", if both y and new_y be negative then the probability of taking next point is high even if T be small enough. for example
as you can see at the step 891 both y and new_y are negative and we take the new_y, however T is 0.109
Again the problem is, the probability formula given in the pseudo code is same as the probability formula which I used in my code
It seems the neighbour is not optimal.
def random_neighbour(x, func_interval, fraction=1):
"""Move a little bit x, from the left or the right."""
amplitude = (max(func_interval) - min(func_interval)) * 1 / (fraction + 0.1)
delta = 1 * amplitude * (.5 - rn.random_sample())
print(delta)
return clip(x + delta, func_interval)
You need something that will move to left/right with equal probability, but might move more random at beginning of the annealing, and less towards end.
The above is just a proposal that seem to work better.

What's wrong with my maths in my attempt to draw the Mandelbrot set?

I am currently trying to code a programm in python which result in drawing the mandelbrot set. The shape of the fractal looks ok but the outline isn't precise at all. I am trying to figuring if the problem comes from my maths or my code
I tried to raise the max number of iterations (which I called i) to see if the calculation was just too low but it didn't change much.
import numpy as np
from PIL import Image
taille = 1000
nb_points = 500
centre_x = taille/2
centre_y = taille/2
fractale = np.zeros((taille, taille, 3), dtype = np.uint8)
for x in range(-nb_points,nb_points):
x = float(x)
x = x/250
for y in range(-nb_points,nb_points):
y = float(y)
y = y/250
X = float(0)
Y = float(0)
i = 0
module_carre = 0
while module_carre < 4 and i <20 :
X = float(X**2 - Y**2 + x)
Y = float(2*X*Y + y)
i += 1
module_carre = float(X*X + Y*Y)
if module_carre < 4:
i=0
couleur = liste_couleur[i]
fractale[centre_x + x*250,centre_y + y*250] = couleur
imgpil = Image.fromarray(fractale, 'RGB')
imgpil.save("resultat.jpg")
I am French that's why you might not understand everything. I didn't paste all the lines about defining differents shades of blue etc...
I don't understand why my outline is this bad. I believe that it comes from my maths but i don't see any errors.
It's my first time posting on stack overflow and i don't understand evey tools yet. I didn't manage to add the image of the output
Sorry for my English and I hope you'll be able to help me fix my code
The main problem is here:
X = float(X**2 - Y**2 + x)
Y = float(2*X*Y + y)
In the calculation for Y, you want to use the old value of X, but it's already been updated and is no longer available. You could instead do:
new_X = float(X**2 - Y**2 + x)
Y = float(2*X*Y + y)
X = new_X
Or you could update them in parallel:
X, Y = float(X**2 - Y**2 + x), float(2*X*Y + y)
Also, you don't need the calls to float, so all you really need is:
X, Y = X**2 - Y**2 + x, 2*X*Y + y
By the way, Python does have a built-in complex number class, so you could initialize z and Z as z = complex(x, y) and Z = complex(0.0, 0.0).
Then your loop body would have:
Z = Z**2 + z
I also suggest increasing the maximum number of iterations. Limiting it to 20 will give very low resolution. I usually use at least 1000, at least for points near the boundary, when generating high-res images.

Solving Laplace's equation

i'm trying to solve Laplace's equation with a particular geometry (two circular conductors), here's what i've done in python :
from __future__ import division
from pylab import *
from scipy import *
from numpy import *
from matplotlib import *
N1=50 # number of points along x and y
N=2*N1+1 # number of points in total
# in cm.
xc1=4
xc2=9
yc1=0
yc2=0
R1=1.75
R2=9
ecart = 1
a, b = linspace(-1, 19, N), linspace(-10, 10, N)
xa, ya = meshgrid(a, b)
V = zeros_like(xa)
for i in range(N):
for j in range(N):
x, y = xa[i,j], ya[i,j]
if (((x-xc1)**2/(R1**2))+((y-yc1)**2/(R1**2)))<=1 : # potential in the central conductor
V[i,j] = 30
if (((x-xc2)**2/(R2**2))+((y-yc2)**2/(R2**2)))>=1 : # potential in the outer conductor
V[i,j] =0
#draws the potential along X along the axis of symmetry.
Vnew = V.copy()
while ecart > 5*10**-2:
for i in range(1,N-1):
for j in range(1,N-1):
Vnew[i,j] = 0.25*(V[i-1,j] + V[i+1,j] + V [i,j-1] + V[i,j+1])
# convergence criterion
ecart = np.max(np.abs(V - (Vnew))/np.max(V))
print(ecart)
# save in the grid V of the calculated grid
for i in range(N):
for j in range(N):
x, y = xa[i,j], ya[i,j]
if (((x-xc1)**2/(R1**2))+((y-yc1)**2/(R1**2))) > 1 and (((x-xc2)**2/(R2**2))+((y-yc2)**2/(R2**2))) < 1 :
V[i,j] = Vnew[i,j]
it actually works with ecart>5*10**-2, but I would like to do it with ecart>10**-3 and here is the problem, it takes too much time ,actually it never ends...
Does someone have any idea in order to improve the program ?
Thank you in advance !

3D Rotation Maths in Python

I'm writing code to make a machine orient a cutting tool in angle of a given vector. I need to rotate the 3D vector coordinates about the Y axis, I've got the equation figured out, but the code just breaks when I use it. Let me show you.
class Vector:
def __init__(self, i, j, k):
self.i = i
self.j = j
self.k = k
v0 = Vector(0.7844645405527362, 0.19611613513818404, 0.5883484054145521)
def findB():
if v0.i != 0:
angle = atan(v0.i/v0.k)
else:
angle = 0
print(angle, degrees(angle)) --> 0.92729... 53.13010...
v1 = Vector(v0.i, v0.j, v0.k)
# this formula rotates the vector about the j axis (comparable to y axis rotation)
v1.i = v0.i * cos(angle) - v0.k * sin(angle)
v1.k = v0.k * cos(angle) + v0.i * sin(angle)
return angle, v1
machine.b, v1 = findB()
print(v1.i, v1.j, v1.k) --> 1.1102230246251565e-16 0.19611613513818404 0.9805806756909203
I've tested that formula on a calculator and it rotates the coordinates correctly, I even hard coded the values to be:
v1.i = 0.784 * cos(0.927) - 0.588 * sin(0.927)
v1.k = 0.588 * cos(0.927) + 0.784 * sin(0.927)
Hard coding the values produces the correct result, but as soon as I use 'angle', v0.i and v0.k as the variables it gets v1.i incorrect. v1.i should be zero (or nearly zero from rounding). I've tested the same formula on a calculator and it works fine. Does anyone know what's going on?

Categories