Почему доступ к одному элементу в массивном массиве так сильно замедляет мою программу? - PullRequest
1 голос
/ 02 июня 2019

Я пишу симуляцию (симуляция 2D модели Изинга).Требуется выбрать случайный элемент в двумерном массиве и умножить значение на -1.Элементы в массиве 2D являются сайтами вращения, а значение равно +1 или -1.

Я забыл добавить строку кода, которая фактически изменяет сайт вращения.Добавление этой (одной!) Строки кода значительно замедляет работу программы.Все сайты вращения по умолчанию имеют значение -1. ​​

Я попытался отредактировать другие функции, которые выполняют некоторую базовую математику (вычисление полной энергии сайтов вращения, в основном просто умножение и сложение), чтобы скрыть, что -1 переворачивается,изменение некоторых дополнений к вычитаниям для учета этого -1 переворота, тем самым избегая необходимости доступа к этому элементу массива.Все еще замедляет его безумно.

Вот результат команды времени, без переворачивания элемента случайного массива.

реальный 0m1.425s пользователь 0m1.685s sys 0m1.078s

И результат, добавление одной строки кода (показано ниже) для реального 0m26.615s пользователя 0m27.019s sys 0m0.920s

import numpy as np
import matplotlib.pyplot as plt
# As seen in lecture:
# Ising model implementation with Metropolis algorithm
# Based on IsingViz.py from Landau, et al.

N     = (2000, 2000) # number of spin sites                                   
num_steps = 20*N[0]*N[1]  # number of iterations
B     = 0.05         # magnetic field                               
mu    = .33          # g mu (not needed if B=0)
J     = 1.           # exchange energy                              
k     = 1.
t     = 1.
np.random.seed() 
state0 = -1*np.ones(N) # Start with an arbitrary spin configuration
Evals = []
def energy(State, J, mu, B):
    # Energy will call row_energy on every row in State, and on 
    # every row of its transpose.
    total_energy = 0
    for row in State:
        total_energy += row_energy(row, J, mu, B)
    for col in State.transpose():
        total_energy += row_energy(col, J, mu, B)
    return total_energy
def row_energy(S, J, mu, B):
    first_set = np.concatenate([np.array([S[-1]]), S[:-1]])
    FirstTerm = np.sum(-J*first_set[:-1]*first_set[1:])
    SecondTerm = np.sum(-mu*S*B)
    return (FirstTerm + SecondTerm)

def energy_change(S, coor):
    """Determine the change in energy if spin `i` is flipped

    `(i+1) % len(S)` implements the periodic boundary condition.
    """
    x = coor[0]
    y = coor[1]
    # Multiply spin site by all adjacent elements
    S_left  = S[x-1,y]
    S_right = S[(x+1) % len(S),y]
    S_up    = S[x,(y+1) % len(S)]
    S_down  = S[x,y-1]
    return 2*J*S[x,y]*(S_left + S_right + S_up + S_down % len(S)) + 2*B*mu*S[x,y]

def TwoDIsing(state0, num_steps, J, mu, B, kT):
    ES = energy(state0, J, mu, B)
    energy_values = []
    energy_values.append(ES)
    # Contains a copy of the state configuration so we don't have to store
    # 2**N**2 * # of time step elements
    state_configs = np.array([state0, state0])
    deltas = [] # A lighter way of keeping track of how the state changes.
    rands = np.random.randint(2000, size=(num_steps,2))
    count = 1
    for x, y in rands:
        #test_state = state_configs[-1]
        # Trial step: flip spin at one random site
        #test_state[x,y] *= -1.
        state_configs[1][x][y] *= -1
        ET = ES + energy_change(state_configs[-1], (x,y))
        if np.exp((ES-ET)/(kT)) > np.random.random():
            #state_configs[-1] = test_state      # replace the state, or
            ES = ET
            deltas.append((x,y))
        else:
            # advance the previous state forward
            state_configs[-1]=state_configs[-2]
            deltas.append(())
        energy_values.append(ES)
        count += 1
        if count % 1000 == 0: print((count / num_steps)*100," %.....................")
    return state_configs, energy_values, deltas

1 Ответ

0 голосов
/ 03 июня 2019

Ошибка при использовании state_configs для хранения двух копий состояния. Намного быстрее этого не делать. Читайте комментарии для деталей

def TwoDIsing(state0, num_steps, J, mu, B, kT):
    ES = energy(state0, J, mu, B)
    energy_values = []
    energy_values.append(ES)
    # Contains a copy of the state configuration so we don't have to store
    # 2**N**2 * # of time step elements
    #state_configs = np.array([state0, state0])
    deltas = [] # A lighter way of keeping track of how the state changes.
    rands = np.random.randint(2000, size=(num_steps,2))
    count = 1
    for x, y in rands:
        #test_state = state_configs[-1]
        # Trial step: flip spin at one random site
        #test_state[x,y] *= -1.
        state0[x][y] *= -1
        ET = ES + energy_change(state0, (x,y))
        if np.exp((ES-ET)/(kT)) > np.random.random():
            #state_configs[-1] = test_state      # replace the state, or
            ES = ET
            deltas.append((x,y))
        else:
            # advance the previous state forward
            state0[x,y] *= -1
            deltas.append(())
        energy_values.append(ES)
        count += 1
        if count % 1000 == 0: print((count / num_steps)*100," %.....................")
    return state_configs, energy_values, deltas
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...