Cython - 使用stdlib <random>

时间:2016-05-19 08:16:24

标签: python c++ random cython

我尝试使用以下代码使c ++库与cython一起工作:生成随机数生成器的父级和使用它生成均匀分布的随机数的子级。

[rnd.pyx]
cdef extern from "<random>" namespace "std":
   cdef cppclass mt19937:
      mt19937() except +
      mt19937(unsigned int) except +
   cdef cppclass uniform_real_distribution[T]:
      uniform_real_distribution()
      uniform_real_distribution(double, double)
      T operator()(mt19937)

cdef class Child:
   cdef mt19937 *rng
   cdef uniform_real_distribution[double] uniform
   def __cinit__(Child self):
      cdef unsigned int seed = 123154654
      self.rng = NULL
      self.uniform = uniform_real_distribution[double](0.,1.)
   cdef set_rng(Child self, mt19937 rng):
      self.rng = &rng
   def gen_uniform(self):
      return self.uniform(self.rng[0])

cdef class Parent:
   cdef mt19937 rng
   cdef public list children
   def __cinit__(Parent self):
      cdef unsigned int seed = 123154654
      self.rng = mt19937(seed)
      self.children = []
   cpdef make_child(Parent self):
      child = Child()
      child.set_rng(self.rng)
      self.children.append(child)

[rnd.pyxbuild]
import os
from distutils.extension import Extension
dirname = os.path.dirname(__file__)
def make_ext(modname, pyxfilename):
    return Extension(
        name=modname,
        sources=[pyxfilename],
        extra_compile_args=["-std=c++11"],
        language="c++",
        include_dirs=[dirname]
    )

[test.py]
import pyximport
pyximport.install()

from rnd import Child, Parent

p = Parent()

for i in range(300):
   p.make_child()
for child in p.children:
   a = child.gen_uniform()
   print(a)

但是,尽管代码编译正确,但gen_uniform函数的输出实际上并非我所期望的:test.py代码首先生成0.941197317223,然后生成0.743410046664。

此外,虽然我没有设法重现它,但更改Child.gen_uniform,我曾设法获得大于1.0的随机数(很多)

最终在一个更复杂的代码中,我将rng指针从一个类传递给另一个类,我得到正常数字,零,大于一个数字,然后是分段错误。

有人能告诉我问题的来源吗?

1 个答案:

答案 0 :(得分:2)

您的代码

cdef set_rng(Child self, mt19937 rng):
  self.rng = &rng

通过复制传递给它的随机数生成器来创建临时随机数生成器(rng)。它需要一个指向临时的指针,然后临时不再存在于函数的末尾,导致指针无效。

你可能想做的是:

cdef set_rng(Child self, mt19937& rng):
  self.rng = &rng

(即通过引用传递)。这取决于Parent拥有Child和随机数生成器这一事实,所有Child都持有指针。因此,它们都具有相同的寿命。如果这不是真的那么你就麻烦了并且需要重新考虑这个方案(也许考虑共享指针?)。