pymc4のソースコード読んでみた - “Model”, Initial Model Class, sampling and random variable [aafa32d]
コミット
2018/05/30のコミットです。
Initial Model Class, sampling and random variable · pymc-devs/pymc4@aafa32d · GitHub
主に、pymc4の根幹となる Model
と RandomVariable
クラスが作成されています。
以下ファイルが追加されています。
- .vscode/settings.json
- pymc4/init.json
- pymc4/model.py
- pymc4/random_variable.py
- pymc4/sample.py
- test.ipynb
やっと Model
クラスを読んでみます。
Model - model.py
Model
クラスのメソッドは以下の通りです。
- new
- init
- model - @property
- description - @property
- get_contexts - @classmethod
- get_context - @classmethod
- add_random_variable
まずは __new__
メソッドのインスタンス生成部分です。単にインスタンス生成した後は以下3つのパターンで parent
をセットしています。
kwargs
のキーワード引数にmodel
が存在する場合:parent
にそのmodel
インスタンスをセット- クラスメソッドの
get_contexts
が存在する場合:parent
にget_contexts
の末尾をセット - それ以外:
parent
にNoneをセット
def __new__(cls, *args, **kwargs): instance = super(Model, cls).__new__(cls) if kwargs.get('model') is not None: instance.parent = kwargs.get('model') elif cls.get_contexts(): instance.parent = cls.get_contexts()[-1] else: instance.parent = None
__init__
メソッドでは named_vars
フィールドを treedict
インスタンスをセットしているだけみたいです。 parent
がある場合にはそれもセットしています。
def __init__(self, name="", model=None, ): self.name = name if self.parent is not None: self.named_vars = treedict(parent=self.parent.named_vars) else: self.named_vars = treedict()
@property
属性が指定されている以下メソッドは単なるgetterとして用意されているみたいなので、まだどのように使われるかわからないので飛ばします。 description
メソッドは実装もされていません。
@property def model(self): return self @property def decription(self): return
get_contexts
と get_context
は同時に読みます。
cls.contexts
でクラス単位のフィールドをチェックしていますが、これは継承元の Context
クラスで定義されている threading.local()
と等価です。またコメントに記載あるように、クラス単位の contexts
フィールドはスレッドセーフなオブジェクトとなっています。
get_contexts
では単に contexts
フィールドの有無をチェックし、 stack
を初期化 or stack
を返却しているだけです。
get_context
ではcontexts.stack
の末尾のインスタンスを返却しています。配列にアクセスしているため、 IndexError
が発生した場合を想定しています。
@classmethod def get_contexts(cls): # no race-condition here, cls.contexts is a thread-local object # be sure not to override contexts in a subclass however! if not hasattr(cls.contexts, 'stack'): cls.contexts.stack = [] return cls.contexts.stack @classmethod def get_context(cls): """Return the deepest context on the stack.""" try: return cls.get_contexts()[-1] except IndexError: raise TypeError("No context on context stack")
最後に add_random_variable
メソッドです。
named_vars
フィールド内に引数の var
インスタンス(= RandomVariable)をセットしています。すでに存在する場合には ValueError
を投げます。 tree_contains
メソッドを呼んでいるので、 parent
まで辿って存在の有無をチェックをしています。
def add_random_variable(self, var): """Add a random variable to the named variables of the model.""" if self.named_vars.tree_contains(var.name): raise ValueError( "Variable name {} already exists.".format(var.name)) self.named_vars[var.name] = var
TODO
- [ ]
Model
クラス内で__new__
メソッド内でparent
の値をセットしているが、__init__
内で実行する場合との違いは?__init__
内で実行しても問題ないのでは?