使用 dask 进行高效的 n 体模拟
Efficient n-body simulation with dask
一个N-body simulation用于模拟涉及粒子相互作用的物理系统的动力学,或者将问题简化为某种具有物理意义的粒子。粒子可以是气体分子或星系中的恒星。 Dask.bag 提供了一种简单的方法来分布集群中的粒子,例如,给 dask.bag.from_sequence()
一个自定义迭代器,即 returns 一个粒子对象:
class ParticleGenerator():
def __init__(self, num_of_particles, max_position, seed=time.time()):
random.seed(seed)
self.index = -1
self.limit = num_of_particles
self.max_position = max_position
def __iter__(self):
return self
def __next__(self):
self.index += 1
if self.index < self.limit :
return np.array([self.max_position*random.random(), self.max_position*random.random(), self.max_position*random.random()])
else :
raise StopIteration
b = db.from_sequence( ParticleGenerator(1000, 1, seed=123456789) )
在这里,粒子对象只是一个 numpy 数组,但可以是任何东西。现在,要计算所有粒子之间的相互作用,必须共享有关位置、速度和类似数量的信息。 dask.bag.map
在集合中的所有元素之间映射一个函数,在这个函数中,计算元素与所有其他粒子之间的相互作用以获得新的粒子状态。
b = b.map(update_position, others=list(b))
b.compute()
为了完整起见,这是 update_position
函数:
def update_position(e, others=None, mass=1, dt=1e-4):
f = np.zeros(3)
for o in others:
r = e - o
r_mag = np.sqrt(r.dot(r))
if r_mag == 0 :
continue
f += ( A/(r_mag**7) + B/(r_mag**13) ) * r
return e + f * (dt**2 / mass)
A
和 B
一些 任意 值。 dask.bag.map()
可以在循环中多次调用以执行模拟。
Dask.bag
是处理这类问题的好集合(抽象)吗?也许 Dask.distributed 是个更好的主意?
- 以这种方式对模拟进行编程,调度程序是否处理所有通信或有关位置、速度等的信息与工作间通信共享?
- 有优化代码的意见吗?特别是关于在调用
dask.bag.map()
时将集合转换为列表的过热问题。
一般来说,N 体模拟需要复杂的算法和数据结构才能 运行 高效。许多常见的解决方案包括使用复杂的树数据结构。您可能想要搜索诸如 kd-tree 或 barnes-hut 之类的术语。
另一方面,Dask.bag 是您可以想象的 simplest/dumbest 并行编程抽象之一,类似于 MapReduce 和 Spark 等其他大容量数据处理系统。这些系统不够灵活,无法在 N 体模拟等复杂问题上提供良好的性能。
像 dask.array or dask.delayed 这样的东西会提供更多的灵活性,但即使是这些也不会与经过微调的 KD 树相同。
一个N-body simulation用于模拟涉及粒子相互作用的物理系统的动力学,或者将问题简化为某种具有物理意义的粒子。粒子可以是气体分子或星系中的恒星。 Dask.bag 提供了一种简单的方法来分布集群中的粒子,例如,给 dask.bag.from_sequence()
一个自定义迭代器,即 returns 一个粒子对象:
class ParticleGenerator():
def __init__(self, num_of_particles, max_position, seed=time.time()):
random.seed(seed)
self.index = -1
self.limit = num_of_particles
self.max_position = max_position
def __iter__(self):
return self
def __next__(self):
self.index += 1
if self.index < self.limit :
return np.array([self.max_position*random.random(), self.max_position*random.random(), self.max_position*random.random()])
else :
raise StopIteration
b = db.from_sequence( ParticleGenerator(1000, 1, seed=123456789) )
在这里,粒子对象只是一个 numpy 数组,但可以是任何东西。现在,要计算所有粒子之间的相互作用,必须共享有关位置、速度和类似数量的信息。 dask.bag.map
在集合中的所有元素之间映射一个函数,在这个函数中,计算元素与所有其他粒子之间的相互作用以获得新的粒子状态。
b = b.map(update_position, others=list(b))
b.compute()
为了完整起见,这是 update_position
函数:
def update_position(e, others=None, mass=1, dt=1e-4):
f = np.zeros(3)
for o in others:
r = e - o
r_mag = np.sqrt(r.dot(r))
if r_mag == 0 :
continue
f += ( A/(r_mag**7) + B/(r_mag**13) ) * r
return e + f * (dt**2 / mass)
A
和 B
一些 任意 值。 dask.bag.map()
可以在循环中多次调用以执行模拟。
Dask.bag
是处理这类问题的好集合(抽象)吗?也许 Dask.distributed 是个更好的主意?- 以这种方式对模拟进行编程,调度程序是否处理所有通信或有关位置、速度等的信息与工作间通信共享?
- 有优化代码的意见吗?特别是关于在调用
dask.bag.map()
时将集合转换为列表的过热问题。
一般来说,N 体模拟需要复杂的算法和数据结构才能 运行 高效。许多常见的解决方案包括使用复杂的树数据结构。您可能想要搜索诸如 kd-tree 或 barnes-hut 之类的术语。
另一方面,Dask.bag 是您可以想象的 simplest/dumbest 并行编程抽象之一,类似于 MapReduce 和 Spark 等其他大容量数据处理系统。这些系统不够灵活,无法在 N 体模拟等复杂问题上提供良好的性能。
像 dask.array or dask.delayed 这样的东西会提供更多的灵活性,但即使是这些也不会与经过微调的 KD 树相同。