2016-05-19 2 views
0

私はC++ライブラリを乱数ジェネレータを生成する親と均等に分布する乱数を生成するために使用する子を使ってcythonで動作させようとしています。コードが正しくコンパイルもののCython - stdlibを使って<random>

[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を変更し、それを再現するために管理していないのに

また、私はかつて大きな乱数(あまり)を取得するために管理している私からrngポインタを渡し、より複雑なコードでは最終的に1.0

私は普通の数字、ゼロ、1より大きい数値、次にセグメント化の誤りを得る。

誰かが問題の原因を教えてもらえますか?

答えて

2

あなたのコード

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

は、それに渡された乱数ジェネレータをコピーすることによって、一時的な乱数生成器(rng)を作成します。それは一時的なものへのポインタを受け取り、一時的に関数の終わりに存在しなくなり、ポインタが無効になります。あなたはおそらく行うには何を意味

は次のとおりです。

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

(すなわち、参照することにより、それを渡します)。これは、ParentChildと、すべてのChildが指すポインタを保持する乱数ジェネレータの両方を所有しているという事実に依存しています。したがって、それらはすべて同じ寿命を有する。これが真実でないなら、あなたは困っており、このスキームを考え直す必要があります(おそらく共有ポインタを考慮していますか?)。

+0

ありがとうございました! (これはあまりにも明白ですが、私のC++は本当に錆びていると思います...) – Silmathoron

+0

本当に、生涯で間違っているものを少し詳しく説明できますか?この例ではうまくいきますが、私の完全なコードでは、私は子供たちに孫を抱き、「親の子供のために」子供として繰り返します:子供の孫のために。子供たち: ''ループの最後にセグメンテーションを与えます'' parent.children [i] .children [j] ''を使って直接アクセスすると、segfaultが返されません... – Silmathoron

+0

どこで起こっているのかすぐには推測できません。最も良い説明は: 'Parent'と' Child'はPythonクラスであり、その寿命はPython refcountingメカニズムによって制御されます(安全に使用する必要があります)。危険なのは 'Child'(おそらく' grandchild')が親の一部である乱数ジェネレータへのポインタを持っているのですが、これは親を生かしていないCポインタ(Pythonのリファレンスではない)ですからです。したがって、あなたがもはや 'parent'へのPythonリファレンスを保持しなくても、' Child'を保持している場合、 'Child'は無効なポインタを持つ可能性があります。 – DavidW

関連する問題