Я работаю над симуляцией физики. Я на самом деле пытаюсь реализовать кластерный алгоритм Вольфа для модели Изинга. Это касается главным образом манипулирования массивами NumPy на стороне программирования. Я хочу составить сюжет с указанием c тепла, энергии, намагниченности с температурой. Для разных температур моделирование полностью независимое. Итак, я попытался распараллелить операцию с помощью функции numba prange. Но это не позволяет. Кажется, причина в другой функции, которую я вызываю из моей основной функции.
Мой код здесь:
# -*- coding: utf-8 -*-
"""
Created on Mon Mar 30 18:39:45 2020
@author: ENDEAVOUR
"""
#%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from numba import jit,prange
import timeit
#----------------------------------------------------------------------
## semi-block codes################
#----------------------------------------------------------------------
start = timeit.default_timer()
error_model=True
@jit(error_model="numpy",nopython=True)
def initialstate(N):
''' generates a random spin configuration for initial condition'''
state =2*np.random.randint(0,2, size=(N,N))-1
return state
@jit(error_model="numpy",nopython=True)
def calcEnergy(config):
'''Energy of a given configuration'''
N=len(config)
energy = 0
for i in range(len(config)):
for j in range(len(config)):
S = config[i,j]
nb = config[(i+1)%N, j] + config[i,(j+1)%N] + config[(i-1)%N, j] + config[i,(j-1)%N]
energy += -nb*S - H*S
return energy/(4.)
@jit(error_model="numpy",nopython=True)
def calcMag(config):
'''Magnetization of a given configuration'''
mag = np.sum(config) + H
return mag
def wolff(config,p):##wollff cluster implementation
## the followng is the function for a sngle run of cluster making and flipping.
#print(config)
'''Monte Carlo move using Wolff-Cluster Sampling'''
que = [] ### "que" ; stores the list of coordinates of the neighbours aded to the cluster
(x0,y0) = np.random.randint(len(config)),np.random.randint(len(config)) ## a random spin is chosen at first
que.append((x0,y0)) ## then added to the "que"
while (len(que) > 0):## as mentioned in the documents I havesnt u , code must run untill there is nobody left to be added
#print('Length of que ',len(que))
q = que[np.random.randint(len(que))] ## a random element from que is chosen
neighbours = [((q[0]+1)%N,q[1]), ((q[0]-1)%N,q[1]), (q[0],(q[1]+1)%N), (q[0],(q[1]-1)%N) ] ## neighbours to the selected spin are considered
for c in neighbours:
if all([config[q]==config[c] , c not in que,np.random.rand() < p]):
## process of adding spins to the que based on wolff's criteria if they have same spin
que.append(c)
config[q] *= -1 ## the spin'q' that was selected from the que is flipped so to avoid being selected in future
que.remove(q) ## the spin'q' is removed from the 'que'
return config
@jit(error_model="numpy",parallel=True)
def run_simulation(N,eqSteps,mcSteps,T,nt):
E,M,C,X = np.empty(nt), np.empty(nt), np.empty(nt), np.empty(nt)
n1, n2 = 1.0/(mcSteps*N*N), 1.0/(mcSteps*mcSteps*N*N)
for tt in prange(nt):
E1 = M1 = E2 = M2 = 0
config = initialstate(N)
iT=1.0/T[tt]; iT2=iT*iT;
p = 1 - np.exp(-2*J*iT)
for i in range(eqSteps): # equilibrate
config=wolff(config,p) # Monte Carlo moves
for i in range(mcSteps):
config=wolff(config,p)
Ene = calcEnergy(config) # calculate the energy
Mag = abs(calcMag(config)) # calculate the magnetisation
E1 = E1 + Ene
M1 = M1 + Mag
M2 = M2 + Mag*Mag
E2 = E2 + Ene*Ene
E[tt] = n1*E1
M[tt] = n1*M1
C[tt] = (n1*E2 - n2*E1*E1)*iT2
X[tt] = (n1*M2 - n2*M1*M1)*iT
print ("Temp:",T[tt],":", E[tt], M[tt],C[tt],X[tt])
return E,M,C,X
#def control():
####################################
N = 64 #N X N spin field
J = 1
H = 0
nt = 50
eqSteps = 150
mcSteps = 20
#############################################No change rquired here
T = np.linspace(1.33, 4.8, nt)
E,M,C,X=run_simulation(N,eqSteps,mcSteps,T,nt)
f = plt.figure(figsize=(25, 20)); # plot the calculated values
plt.title("External Field :%5.2f"%(H))
sp = f.add_subplot(3, 2, 1 );
plt.scatter(T, E, s=50, marker='o', color='Red')
plt.xlabel("Temperature (T)", fontsize=20);
plt.ylabel("Energy ", fontsize=20); plt.axis('tight');
sp = f.add_subplot(3, 2, 2 );
plt.scatter(T, M, s=50, marker='o', color='Blue')
plt.xlabel("Temperature (T)", fontsize=20);
plt.ylabel("Magnetization ", fontsize=20); plt.axis('tight');
sp = f.add_subplot(3, 2, 3 );
plt.scatter(T, C, s=50, marker='o', color='Red')
plt.xlabel("Temperature (T)", fontsize=20);
plt.ylabel("Specific Heat ", fontsize=20); plt.axis('tight');
sp = f.add_subplot(3, 2, 4 );
plt.scatter(T, X, s=50, marker='o', color='RoyalBlue')
plt.xlabel("Temperature (T)", fontsize=20);
plt.ylabel("Susceptibility", fontsize=20); plt.axis('tight');
sp = f.add_subplot(3 ,2 ,5);
plt.scatter(E, M,s=50, marker='+', color='Red')
plt.xlabel("Energy ", fontsize=20); plt.axis('tight');
plt.ylabel("Magnetization ", fontsize=20); plt.axis('tight');
plt.show()
stop = timeit.default_timer()
print('Time taken for this simulation:: ', stop - start)
Отображаемое сообщение об ошибке:
E:/Project/wolff_cluster.2.py:78: NumbaWarning:
Compilation is falling back to object mode WITH looplifting enabled because Function "run_simulation" failed type inference due to: Untyped global name 'wolff': cannot determine Numba type of <class 'function'>
File "wolff_cluster.2.py", line 94:
def run_simulation(N,eqSteps,mcSteps,T,nt):
<source elided>
for i in range(eqSteps): # equilibrate
config=wolff(config,p) # Monte Carlo moves
^
@jit(error_model="numpy",parallel=True,nopython=True)
E:/Project/wolff_cluster.2.py:78: NumbaWarning:
Compilation is falling back to object mode WITHOUT looplifting enabled because Function "run_simulation" failed type inference due to: cannot determine Numba type of <class 'numba.dispatcher.LiftedLoop'>
File "wolff_cluster.2.py", line 86:
def run_simulation(N,eqSteps,mcSteps,T,nt):
<source elided>
for tt in prange(nt):
^
@jit(error_model="numpy",parallel=True,nopython=True)
C:\Users\Sandipan\Anaconda3\lib\site-packages\numba\compiler.py:725: NumbaWarning: Function "run_simulation" was compiled in object mode without forceobj=True, but has lifted loops.
File "wolff_cluster.2.py", line 79:
@jit(error_model="numpy",parallel=True,nopython=True)
def run_simulation(N,eqSteps,mcSteps,T,nt):
^
self.func_ir.loc))
C:\Users\Sandipan\Anaconda3\lib\site-packages\numba\compiler.py:734: NumbaDeprecationWarning:
Fall-back from the nopython compilation path to the object mode compilation path has been detected, this is deprecated behaviour.
For more information visit http://numba.pydata.org/numba-doc/latest/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit
File "wolff_cluster.2.py", line 79:
@jit(error_model="numpy",parallel=True,nopython=True)
def run_simulation(N,eqSteps,mcSteps,T,nt):
^
warnings.warn(errors.NumbaDeprecationWarning(msg, self.func_ir.loc))
E:/Project/wolff_cluster.2.py:78: NumbaWarning:
Compilation is falling back to object mode WITHOUT looplifting enabled because Function "run_simulation" failed type inference due to: Untyped global name 'wolff': cannot determine Numba type of <class 'function'>
File "wolff_cluster.2.py", line 94:
def run_simulation(N,eqSteps,mcSteps,T,nt):
<source elided>
for i in range(eqSteps): # equilibrate
config=wolff(config,p) # Monte Carlo moves
^
@jit(error_model="numpy",parallel=True,nopython=True)
C:\Users\Sandipan\Anaconda3\lib\site-packages\numba\compiler.py:725: NumbaWarning: Function "run_simulation" was compiled in object mode without forceobj=True.
File "wolff_cluster.2.py", line 86:
def run_simulation(N,eqSteps,mcSteps,T,nt):
<source elided>
for tt in prange(nt):
^
self.func_ir.loc))
C:\Users\Sandipan\Anaconda3\lib\site-packages\numba\compiler.py:734: NumbaDeprecationWarning:
Fall-back from the nopython compilation path to the object mode compilation path has been detected, this is deprecated behaviour.
For more information visit http://numba.pydata.org/numba-doc/latest/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit
File "wolff_cluster.2.py", line 86:
def run_simulation(N,eqSteps,mcSteps,T,nt):
<source elided>
for tt in prange(nt):
^
warnings.warn(errors.NumbaDeprecationWarning(msg, self.func_ir.loc))
E:/Project/wolff_cluster.2.py:78: NumbaWarning:
Compilation is falling back to object mode WITHOUT looplifting enabled because Function "run_simulation" failed type inference due to: Untyped global name 'wolff': cannot determine Numba type of <class 'function'>
File "wolff_cluster.2.py", line 94:
def run_simulation(N,eqSteps,mcSteps,T,nt):
<source elided>
for i in range(eqSteps): # equilibrate
config=wolff(config,p) # Monte Carlo moves
^
@jit(error_model="numpy",parallel=True,nopython=True)
Temp: 1.33 : -0.9890869140625 0.9942626953125 0.041807132522607905 0.017099021969053343
После показа вышеуказанная ошибка работает как обычно, но занимает 3 раза по сравнению с чистой версией python. Я пытался без вумбы для Вольфа, но безуспешно. Действительно важно распараллелить для l oop в run_simulation, поскольку это самая трудоемкая часть кода.
Я думаю, что проблема с функцией Вольфа. Numba не может ничего сделать, чтобы оптимизировать это. Но я не использовал ни одной функции, не поддерживаемой Numba. (простой список и только numpy). Он помечает условие if - ключевое слово «all». Я пробовал с 'и' внутри if, но тогда программа сталкивается с бесконечным l oop. Он добавляет один и тот же элемент несколько раз в очередь (вероятно, пропускает не в состоянии). Я знаю, что это технически невозможно. (Оба 'и' & 'all' должны работать). Я делаю что-то не так. Но я понятия не имею, что это такое.
Не могли бы вы помочь мне с оптимизацией этого кода. Работает нормально без нумбы. Но при N> 100 он будет слишком медленным для любой полезной работы.