Проблемы пространства имен параллельных вычислений IPython

Я читал и перечитывал документацию/учебник IPython, и я не могу понять проблему с этим конкретным фрагментом кода. Кажется, что функция dimensionless_run не видна в пространстве имен, доставляемом каждому из движков, но я смущен, потому что функция определена в __main__ и явно видна как часть глобального пространства имен.

wrapper.py:

import math, os

def dimensionless_run(inputs):
    output_file = open(inputs['fn'],'w')
    ...
    return output_stats

def parallel_run(inputs):
    import math, os  ## Removing this line causes a NameError: global name 'math'
                     ## is not defined.
    folder = inputs['folder']
    zfill_amt = int(math.floor(math.log10(inputs['num_iters'])))
    for i in range(inputs['num_iters']):
        run_num_str = str(i).zfill(zfill_amt)
        if not os.path.exists(folder + '/'):
            os.mkdir(folder)
        dimensionless_run(inputs)
    return

if __name__ == "__main__":
    inputs = [input1,input2,...]
    client = Client()
    lbview = client.load_balanced_view()
    lbview.block = True
    for x in sorted(globals().items()):
        print x
    lbview.map(parallel_run,inputs)

Выполнение этого кода после ipcluster start --n=6 дает отсортированный глобальный словарь, включая модули math и os, а также функции parallel_run и dimensionless_run. За этим следует IPython.parallel.error.CompositeError: одно или несколько исключений из вызова метода: parallel_run, который состоит из большого количества [n:apply]: NameError: global name 'dimensionless_run' is not defined, где n принимает значение от 0 до 5.

Есть две вещи, которых я не понимаю, и они явно связаны.

  1. Почему код не идентифицирует dimensionless_run в глобальном пространстве имен?
  2. Почему import math, os необходимо внутри определения parallel_run?

Отредактировано: оказалось, что это вовсе не ошибка пространства имен — я выполнял ipcluster start --n=6 в каталоге, который не содержал кода. Чтобы исправить это, все, что мне нужно было сделать, это выполнить команду запуска в каталоге моего кода. Я также исправил это, добавив строки:

    inputs = input_pairs
    os.system("ipcluster start -n 6") #NEW
    client = Client()
    ...
    lbview.map(parallel_run,inputs)
    os.system("ipcluster stop")       #NEW

которые запускают нужный кластер в нужном месте.


person Michael K    schedule 06.09.2012    source источник


Ответы (1)


В основном это дубликат проблем с пространством имен Python с IPython.parallel, в котором есть более подробный ответ, но суть:

Когда клиент отправляет parallel_run движку, он отправляет только эту функцию, а не все пространство имен, в котором определена функция (модуль __main__). Таким образом, при запуске удаленного parallel_run поиск math или os или dimensionless_run будет сначала искать в locals() (то, что уже определено в функции, т. е. ваш импорт внутри функции), а затем в globals(), который является модулем __main__ на двигателе.

Существуют различные подходы к обеспечению доступности имен в механизмах, но, возможно, самый простой — это явно определить/отправить их в механизмы (интерактивное пространство имен находится __main__ на механизмах, точно так же, как локально в IPython):

client[:].execute("import os, math")
client[:]['dimensionless_run'] = dimensionless_run

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

Это проблема, уникальная для модулей, определенных интерактивно/в скрипте. Она не возникает, если этот файл является модулем вместо скрипта, например

from mymod import parallel_run
lbview.map(parallel_run, inputs)

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

person minrk    schedule 06.09.2012
comment
Оба эти решения работают наполовину, добавляя dimensionless_run в пространство имен. Однако функции и классы, вызываемые dimensionless_run, теперь появляются как отсутствующие в пространстве имен. Должен быть эффективный/питоновский способ отправки этих данных в движки, без написания строки кода для каждой функции, которую необходимо передать. - person Michael K; 07.09.2012
comment
Я использую один из трех подходов, каждый из которых приводит к созданию полных пространств имен: 1. определяю функции удаленно в первую очередь с помощью магии %%px (я делаю это при использовании блокнота IPython). 2. использовать модули, где эта проблема никогда не возникает. 3. сделать все функции, определенные локально, но предназначенные для удаленного использования, никогда не разрешать имена вне глобальных переменных (100% имен являются аргументами функции или импортируются внутри функции). Еще один вариант при работе с таким скриптом, как ваш, - просто нажать globals() (конечно, исключая сам объект Client). - person minrk; 07.09.2012
comment
Хм, я переместил весь код определения функции в модуль и переместил if __name__ == "__main__" from perceptions_wrapper import parallel_run import time, sys from IPython.parallel import Client - person Michael K; 07.09.2012
comment
(Извините за двойной пост) Хм, я переместил весь код определения функции в модуль и переместил код if __name__ == "__main__"... в собственный скрипт, который зависит только от: from wrapper import parallel_run from IPython.parallel import Client, и я все еще получаю ту же ошибку. Если я удалю строку import math, os из parallel_run (что, кажется, я должен сделать, если я импортирую из модуля), они станут первыми объектами, отсутствующими в пространстве имен. - person Michael K; 07.09.2012
comment
Это странно, и не было моего опыта. Если бы это не сработало, то вызов приложения с любой функцией модуля (например, numpy.linalg.norm или json.dumps) тоже не сработал бы, и они явно работают, поэтому я не знаю, чем отличается то, что вы делаете, не видя этого. Пожалуйста, предоставьте полный пример, который воспроизводит вашу проблему, и, возможно, отправьте его на ipython-dev или GitHub, которые являются более подходящими местами для подробного обсуждения таких вещей. - person minrk; 07.09.2012
comment
О, быстрый вопрос: Когда вы делаете это модулем, это просто в cwd? И если да, то одинаковы ли cwd для скрипта и ваших движков? Чтобы импорт модуля работал, модуль должен быть доступен для импорта в обеих средах. - person minrk; 07.09.2012