Векторизация симуляции Монте-Карло на Python

Недавно я работал над некоторым кодом на Python, чтобы моделировать двумерную калибровочную теорию U (1) с использованием методов Монте-Карло. По сути, у меня есть массив n x n на 2 (назовите его Link) унитарных комплексных чисел (их величина равна единице). Я случайным образом выбираю элемент своего массива ссылок и предлагаю случайное изменение числа на этом сайте. Затем я вычисляю результирующее изменение действия, которое могло бы произойти из-за этого изменения. Затем я принимаю изменение с вероятностью, равной min (1, exp (-dS)), где dS - это изменение действия. Код итератора следующий

def iteration(j1,B0):
    global Link
    Staple = np.zeros((2),dtype=complex)
    for i0 in range(0,j1):
        x1 = np.random.randint(0,n)
        y1 = np.random.randint(0,n)
        u1 = np.random.randint(0,1)
        Linkrxp1 = np.roll(Link,-1, axis = 0)
        Linkrxn1 = np.roll(Link, 1, axis = 0)
        Linkrtp1 = np.roll(Link, -1, axis = 1)
        Linkrtn1 = np.roll(Link, 1, axis = 1)
        Linkrxp1tn1 = np.roll(np.roll(Link, -1, axis = 0),1, axis = 1)
        Linkrxn1tp1 = np.roll(np.roll(Link, 1, axis = 0),-1, axis = 1)


        Staple[0] = Linkrxp1[x1,y1,1]*Linkrtp1[x1,y1,0].conj()*Link[x1,y1,1].conj() + Linkrxp1tn1[x1,y1,1].conj()*Linkrtn1[x1,y1,0].conj()*Linkrtn1[x1,y1,1]
        Staple[1] = Linkrtp1[x1,y1,0]*Linkrxp1[x1,y1,1].conj()*Link[x1,y1,0].conj() + Linkrxn1tp1[x1,y1,0].conj()*Linkrxn1[x1,y1,1].conj()*Linkrxn1[x1,y1,0]


        uni = unitary()
        Linkprop = uni*Link[x1,y1,u1]
        dE3 = (Linkprop - Link[x1,y1,u1])*Staple[u1]
        dE1 = B0*np.real(dE3)
        d1 = np.random.binomial(1, np.minimum(np.exp(dE1),1))


        d = np.random.uniform(low=0,high=1)
        if d1 >= d:
            Link[x1,y1,u1] = Linkprop
        else:
            Link[x1,y1,u1] = Link[x1,y1,u1]

В начале программы я вызываю процедуру под названием "randomize" для генерации K случайных унитарных комплексных чисел, которые имеют небольшие мнимые части, и сохраняю их в массиве под названием Cnum длины K. В той же процедуре я также просматриваю свой массив Link и устанавливаю каждый элемент к случайному унитарному комплексному числу. Код указан ниже.

def randommatrix():
    global Cnum
    global Link
    for i1 in range(0,K):
        C1 = np.random.normal(0,1)
        Cnum[i1] = np.cos(C1) + 1j*np.sin(C1)
        Cnum[i1+K] = np.cos(C1) - 1j*np.sin(C1)
    for i3,i4 in itertools.product(range(0,n),range(0,n)):
        C2 = np.random.uniform(low=0, high = 2*np.pi)
        Link[i3,i4,0] = np.cos(C2) + 1j*np.sin(C2)
        C2 = np.random.uniform(low=0, high = 2*np.pi)
        Link[i3,i4,1] = np.cos(C2) + 1j*np.sin(C2)

Следующая процедура используется во время итерационной процедуры для получения случайного комплексного числа с небольшой мнимой частью (путем извлечения случайного элемента массива Cnum, который мы сгенерировали ранее).

def unitary():
    I1 = np.random.randint((0),(2*K-1))
    mat = Cnum[I1]
    return mat

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

def Plq(j1,B0):
    i5 = 0
    Lboot = np.zeros(j1)
    while i5<j1:
        iteration(25000,B0)
        Linkrxp1 = np.roll(Link,-1, axis = 0)
        Linkrtp1 = np.roll(Link, -1, axis = 1)
        c0 = np.real(Link[:,:,0]*Linkrxp1[:,:,1]*Linkrtp1[:,:,0].conj()*Link[:,:,1].conj())
        i5 = i5 + 1

Нам нужно определить некоторые переменные, прежде чем что-либо запускать, поэтому вот начальные переменные, которые я определяю перед определением любых подпрограмм.

K = 20000
n = 50
a = 1.0
Link = np.zeros((n,n,2),dtype = complex)
Cnum = np.zeros((2*K), dtype = complex)

Этот код работает, но он очень медленный. Есть ли способ использовать многопроцессорность или что-то еще, чтобы ускорить это?


person chuckstables    schedule 14.08.2018    source источник
comment
Пожалуйста, сделайте отступ в коде внутри функции должным образом, чтобы другие могли его скопировать.   -  person Sheldore    schedule 15.08.2018
comment
Сделайте отступы правильно. Затем покажите нам пример того, как вызывается эта процедура, с полными данными в коде или в файле. Прочтите и следуйте Как создать минимальный, полный и проверяемый пример.   -  person Rory Daulton    schedule 15.08.2018
comment
И Базинге, и Рори; мои извенения. Я исправил отступы и прочитал предоставленную ссылку. Вскоре я отредактирую оставшуюся часть сообщения, чтобы дать пример того, как вызывается подпрограмма. @RoryDaulton, вы хотите, чтобы я опубликовал весь код, который понадобится кому-то для запуска кода на своей машине?   -  person chuckstables    schedule 15.08.2018
comment
Не могли бы вы предоставить один работающий блок кода?   -  person Joe    schedule 15.08.2018
comment
Да, покажите достаточно кода, чтобы его можно было скопировать и вставить в редактор, а затем запустить код. Это не обязательно должен быть весь ваш код, достаточно, чтобы показать вашу проблему.   -  person Rory Daulton    schedule 15.08.2018
comment
Как ускорить np.roll ()? Я так и не успел написать там ответ на свой вопрос, но в этот ответ Я показываю, как scipy.ndimage.convolve будет выполнять некоторые типы суммирования, которые используют много np.roll быстрее. Он вызывает функции, написанные на скомпилированном языке (FORTRAN или C), и поэтому может быть быстрее, чем множество строк python внутри циклов python, особенно когда вам нужно повторно создавать экземпляры массивов numpy на каждой итерации.   -  person uhoh    schedule 01.04.2019


Ответы (1)


Вам следует использовать типы данных cython и c. Другая ссылка на cython. Он создан для быстрых вычислений.

Вы можете использовать многопроцессорность потенциально в одном из двух случаев. Если у вас есть один объект, который необходимо совместно использовать нескольким процессам, вам потребуется использовать Manager (см. Ссылку на многопроцессорную обработку), Lock и Array для совместного использования объекта между процессами. Однако нет гарантии, что это приведет к увеличению скорости, поскольку каждый процесс должен заблокировать ссылку, чтобы гарантировать ваш прогноз, при условии, что на прогнозы влияют все элементы в ссылке (если процесс изменяет элемент одновременно с другим процессом делает прогноз для элемента, прогноз не будет основан на самой последней информации).

Если ваши прогнозы не учитывают состояние других элементов, то есть он заботится только об одном элементе, тогда вы можете разбить свой массив Link на сегменты и блоки divvy на несколько процессов в пуле процессов, а когда это будет сделано, объедините сегменты обратно в один массив. Это, безусловно, сэкономит время, и вам не придется использовать какие-либо дополнительные механизмы многопроцессорной обработки.

person darrahts    schedule 15.08.2018