Не уверен, есть ли у вас какие-либо ограничения с точки зрения зависимостей, но я бы определенно рассмотрел Numba .Он предоставляет набор декораторов (в частности, njit
), которые скомпилируют ваш код в машинный код и сделают его потенциально намного, гораздо быстрее, если вы используете совместимые типы (например, массивы numpy, но не pandas DataFrames).
Кроме того, не уверен, в каком масштабе вы смотрите, но я вполне могу найти примеры гораздо более оптимизированных алгоритмов простого поиска, чем ручной цикл for.
В противном случае вы всегда можете упасть-вернемся на Cython, но это требует переписывания вашего кода.
EDIT : хорошо, я попробовал numba.
Несколько замечаний:
- переместил весь цикл for внутри функции, так что вы можете украсить его с помощью
njit
- в
Neighbors
, мне пришлось изменить rows
и cols
от list
с до np.array
с, потому что numba не принимает индексирование через списки - Я заменил
np.random.random_integers
на np.random.randint
, так как первый устарел - Я заменил
math.exp
наnp.exp
, что должно дать небольшое повышение производительности (лучшеИдес экономит вам импорт)
import numpy as np
from numba import njit
def largest_primes_under(N):
n = N - 1
while n >= 2:
if all(n % d for d in range(2, int(n ** 0.5 + 1))):
return n
n -= 1
@njit
def Neighbors(Lattice,i,j,n=1):
''' Returns an flat array of all neighboring sites in the n-th coordination sphere including the center'''
N, M = Lattice.shape
rows = np.array([(i-1) % N, i, (i+1) % N])
cols = np.array([(j-1) % N, j, (j+1) % M])
return Lattice[rows,:][:,cols].flatten()
@njit
def calc_dE(Lattice, x, y, z):
N, M = Lattice.shape
old_energy = 0
new_energy = 0
for i in [0,1,-1]:
for j in [0,1,-1]:
if i == 0 and j == 0:
continue
if Lattice[x%N,y%M] == Lattice[(x+i)%N,(y+j)%M]:
old_energy += 1
elif z == Lattice[(x+i)%N,(y+j)%M]:
new_energy += 1
return old_energy-new_energy
@njit
def fun(L, MCS, a):
N, M = L.shape
for t in range(1,MCS+1):
rand = np.random.randint(N*M)
for i in range(0,N**2):
index = (a*i + rand) % (N**2)
x = index % N
y = index // N
n = Neighbors(L,x,y)
if len(n)-1 == 0: continue
else: z = np.random.choice(n)
dE = calc_dE(L,x,y,z)
if (dE < 0): L[x%N,y%N] = z
elif np.random.sample() < np.exp(-dE*2.5): L[x%N,y%N] = z
return L
Выполнение того же примера
N, M = 100,100
orientations = N*M
MCS = 1
L = np.random.randint(1,orientations+1,size=(N,M))
a = largest_primes_under(N*M)
через %timeit fun(L, MCS, a)
(в Jupyter) дает мне
16.9 ms ± 853 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
что в ~ 15 раз быстрее, чем у вас сейчас.Вероятно, вы можете сделать больше оптимизаций, но в numba приятно то, что я получил ускорение в 15 раз, не углубляясь и не меняя при этом, как реализован ваш код.
Пара общих замечаний:
- в
Neighbors
, аргумент / параметр n
не используется, поэтому вы должны удалить его для ясности (или обновить код) - в Python, вы обычно хотите использовать нижний регистр дляимена функций и переменных.Прописные буквы обычно зарезервированы для классов (не объектов) и «глобальных» переменных
- Ваш комментарий о том, что
largest_primes_under
вызывается только один раз, определенно уместен, мне следовало бы лучше посмотреть код.
преждевременная оптимизация - корень всего зла