Как передать ** kwargs в другую функцию с измененными значениями для этого ключа? - PullRequest
0 голосов
/ 19 января 2019

У меня есть следующая функция, которая рассчитывает распространение лазерного луча в резонаторе. Это зависит от многих параметров, которые хранятся в файле dict core_data, который является базовым набором параметров.

def propagate(N, core_data, **ddata):
    cd = copy.deepcopy(core_data)  # use initial configuration
    cd.update(ddata)  # update with data I want to change
    cavity = get_new_cavity(cd)  # get new cavity object
    P = []
    for i in range(N):
        cavity.evolve(1)
        P.append(cavity.get_power())
    return P

Если я хочу изменить параметр и увидеть его влияние на лазер, я могу просто вызвать функцию, например,

P0 = propagate(1000, core_data, L1=1.2, M5=17)

Это работает очень хорошо.

Теперь я бы написал функцию, которая передает эту функцию в ProcessPoolExecutor, при этом значения **ddata повторяются с использованием того же ключа. Это должно работать, например, так (более простой пример):

propagate_parallel(1000, core_data,
                   L1=np.linspace(1, 2, 2),
                   M5=np.linspace(16, 17, 2))

А затем следует сделать это параллельно:

propagate(1000, core_data, L1=1, M5=16)
propagate(1000, core_data, L1=1, M5=17)
propagate(1000, core_data, L1=2, M5=16)
propagate(1000, core_data, L1=2, M5=17)

Что-то вроде этого работает для моего случая:

xrng = np.linspace(110e-30, 150e-30, Nx)
yrng = np.linspace(6.6e-9, 6.7e-9, Ny)

futures = []
with confu.ProcessPoolExecutor(max_workers=Ncores) as pool:
    for y, x in it.product(yrng, xrng):
        futures.append(pool.submit(propagate, RTs=1000,
                                   core_data=core_data,
                                   gdd_dm=x, dwl_filt=y))

Проблема в том, что это не гибко, и я не могу передать это в хорошую функцию, как написано выше. Это должна быть функция, которую можно вызывать так, чтобы воспроизвести код сверху:

propagate_parallel(1000, core_data, gdd_dm=xrng, dwl_filt=yrng)

Как бы я передал ключи от **ddata с повторяющимися значениями этого соответствующего ключа?

К вашему сведению, я использовал:

import numpy as np
import concurrent.futures as confu
import itertools as it

Ответы [ 2 ]

0 голосов
/ 19 января 2019

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

@ forward87: Ваше первое предложение уже разблокировало меня, и я понял, что **data можно обработать просто через словарь. Итак, в конце мне просто нужно было преобразовать входной dict в список dicts для следующей функции, вот так (с некоторыми несоответствующими частями, которые вырезаются):

def propagate_parallel(RTs, cav_data, **ddata):
    keys = list(ddata.keys())
    values = list(ddata.values())
    futures = []
    res = []
    with confu.ProcessPoolExecutor(max_workers=32) as pool:
        for i in it.product(*values):
            futures.append(pool.submit(propagate, RTs=RTs,
                                       cav_data=cav_data,
                                       **dict(zip(keys, list(i)))))
    for fut in futures:
        res.append(fut)
    return res

В конце концов, я, наконец, понимаю, **kwargs, и что это можно воспринимать как диктат. Спасибо!

0 голосов
/ 19 января 2019

Вы ищете итерации по декартовому произведению.

Вот способ перебрать декартову.

from itertools import product
import numpy as np

L1=np.linspace(1, 2, 2)
M5=np.linspace(16, 17, 2)
dconf = dict(data=5)
size = L1.size
loop_size = size**2

def propagate(N, data, modifiers):
    data.update(modifiers)
    out = []
    for i in range(N):
        out.append('%s : %s : %s : %s'%(i, *data.values()))
    return out

mod = (dict(L1=i, M5=j) for i, j in product(L1, M5))
m = map(propagate, np.arange(2, 2+loop_size), (dconf,)*loop_size, mod)

for outer in m:
    for inner in outer:
        print(inner)

Это вы можете адаптировать к своему коду, и если вам действительно нужно идти параллельно (со всем, что это означает с точки зрения разделения информации между ядрами), возможно, обратите внимание на Dask.

Надеюсь, этого достаточно, чтобы вы пошли.

редактирование: Ваш вопрос довольно сложно определить. Ваш вопрос действительно, как просто выполнить простой «вызов функции»? Полагаю, один из ответов - просто создать функцию обтекания, что-то вроде ...

def propagate(N, data, modifiers):
    ...

def call_propagate(N, data, L1_, M5_):
    mod = ...
    m = map(...
    return m

for outer in call_propagate(1000, dconf, L1, M5)
    for inner in outer:
        print(inner)
...