Описание проблемы
При написании симулятора частиц Монте-Карло (движение буриунов и фотонное излучение) в python/numpy. Мне нужно сохранить выходной файл моделирования ( → 10 ГБ) в файл и обработать данные на втором шаге. Совместимость с Windows и Linux важна.
Число частиц (n_particles) составляет 10-100. Количество шагов времени (time_size) составляет ~ 10 ^ 9.
Моделирование имеет 3 шага (код ниже для версии "все-в-ОЗУ" ):
-
Имитировать (и сохранить) массив значений
emission(содержит много элементов почти-0):- shape (
n_particlesxtime_size), float32, размер 80 ГБ
- shape (
-
Вычислить массив
counts(случайные значения из процесса Пуассона с ранее вычисленными скоростями):-
shape (
n_particlesxtime_size), uint8, размер 20 ГБcounts = np.random.poisson(lam=emission).astype(np.uint8)
-
-
Найти временные метки (или индекс) счетчиков. Графы почти всегда равны 0, поэтому массивы timestamp будут помещаться в ОЗУ.
# Loop across the particles timestamps = [np.nonzero(c) for c in counts]
Я делаю шаг 1 один раз, затем повторяю шаг 2-3 много (~ 100) раз. В будущем мне может понадобиться предварительно обработать emission (применить cumsum или другие функции) до вычисления counts.
Вопрос
У меня есть работающая в памяти реализация, и я пытаюсь понять, что лучше всего подходит для реализации устаревшей версии, которая может масштабировать (намного) более длительные симуляции.
Что бы я хотел:
Мне нужно сохранить массивы в файл, и я хотел бы использовать один файл для моделирования. Мне также нужен "простой" способ хранения и вызова словаря параметра моделирования (скаляры).
В идеале мне нужен массив numpy с поддержкой файлов, который я могу предварительно распределить и заполнить куски. Затем я хотел бы, чтобы методы массива numpy (max, cumsum,...) работали прозрачно, требуя только ключевое слово chunksize, чтобы указать, сколько массива загружается на каждой итерации.
Еще лучше, мне нужен Numexpr, который работает не между кешем и оперативной памятью, а между ОЗУ и жестким диском.
Каковы практические варианты
В качестве первого варианта Я начал экспериментировать с pyTables, но я не доволен его сложностью и абстракциями (так отличается от numpy). Более того, мое текущее решение (см. Ниже) является UGLY и не очень эффективным.
Итак, мои варианты, для которых я ищу ответ,
-
реализовать массив numpy с требуемой функциональностью (как?)
-
использовать pytable более умным способом (разные структуры данных/методы)
-
используйте другую библиотеку: h5py, blaze, pandas... (я пока не пробовал ни одного из них).
Предварительное решение (pyTables)
Я сохраняю параметры моделирования в группе '/parameters': каждый параметр преобразуется в скалярный массив numpy. Подробное решение, но оно работает.
Я сохраняю emission как расширяемый массив (EArray), потому что я генерирую данные в кусках, и мне нужно добавить каждый новый кусок (хотя я знаю конечный размер). Сохранение counts более проблематично. Если сохранить его как массива pytable, сложно выполнить такие запросы, как "counts >= 2". Поэтому я сохранил counts как несколько таблиц (по одной на каждую частицу) [UGLY], и я запрашиваю с .get_where_list('counts >= 2'). Я не уверен, что это экономически эффективно, и
генерируя все эти таблицы вместо использования одного массива, значительно улучшает файл HDF5. Более того, как ни странно, создание этих таблиц требует создания настраиваемого типа dtype (даже для стандартных типов numpy):
dt = np.dtype([('counts', 'u1')])
for ip in xrange(n_particles):
name = "particle_%d" % ip
data_file.create_table(
group, name, description=dt, chunkshape=chunksize,
expectedrows=time_size,
title='Binned timetrace of emitted ph (bin = t_step)'
' - particle_%d' % particle)
Каждая таблица счетчиков с частицами имеет другое имя (name = "particle_%d" % ip) и что мне нужно поместить их в список python для легкой итерации.
EDIT. Результатом этого вопроса является симулятор броуновского движения, называемый PyBroMo.