NumPy 配列のように動作するクラスを使用して自動微分を実装しようとしています。をサブクラス化していませんnumpy.ndarray
が、2 つの配列属性が含まれています。1 つは値用で、もう 1 つはヤコビ行列用です。値とヤコビアンの両方を操作するために、すべての操作がオーバーロードされます。しかし、NumPy ufunc (例: np.log
) をカスタム「配列」で動作させるのに問題があります。
問題を説明する次の最小限の例を作成しました。Two
は、NumPy 配列の耐放射線バージョンであり、すべてを 2 回計算し、結果が等しいことを確認します。
インデックス付け、要素ごとの対数、および長さをサポートする必要があります。普通のようにndarray
。要素ごとの対数は、 using を呼び出すと正常に機能x.cos()
しますが、 using を呼び出すと予期しないことを行いnp.cos(x)
ます。
from __future__ import print_function
import numpy as np
class Two(object):
def __init__(self, val1, val2):
print("init with", val1, val2)
assert np.array_equal(val1, val2)
self.val1 = val1
self.val2 = val2
def __getitem__(self, s):
print("getitem", s, "got", Two(self.val1[s], self.val2[s]))
return Two(self.val1[s], self.val2[s])
def __repr__(self):
return "<<{}, {}>>".format(self.val1, self.val2)
def log(self):
print("log", self)
return Two(np.log(self.val1), np.log(self.val2))
def __len__(self):
print("len", self, "=", self.val1.shape[0])
return self.val1.shape[0]
x = Two(np.array([1,2]).T, np.array([1,2]).T)
インデックスを作成すると、予想どおり、両方の属性から関連する要素が返されます。
>>> print("First element in x:", x[0], "\n")
init with [1 2] [1 2]
init with 1 1
getitem 0 got <<1, 1>>
init with 1 1
First element in x: <<1, 1>>
を使用して呼び出された場合、要素ごとの対数は問題なく機能しx.cos()
ます。
>>> print("--- x.log() ---", x.log(), "\n")
log <<[1 2], [1 2]>>
init with [ 0. 0.69314] [ 0. 0.69314]
--- x.log() --- <<[ 0. 0.69314], [ 0. 0.69314]>>
ただし、np.log(x)
期待どおりに動作しません。オブジェクトに長さがあることを認識するため、すべての項目を抽出し、それぞれの対数を取り、Two オブジェクト (dtype=object) の配列を返します。
>>> print("--- np.log(x) with len ---", np.log(x), "\n") # WTF
len <<[1 2], [1 2]>> = 2
len <<[1 2], [1 2]>> = 2
init with 1 1
getitem 0 got <<1, 1>>
init with 1 1
init with 2 2
getitem 1 got <<2, 2>>
init with 2 2
len <<[1 2], [1 2]>> = 2
len <<[1 2], [1 2]>> = 2
init with 1 1
getitem 0 got <<1, 1>>
init with 1 1
init with 2 2
getitem 1 got <<2, 2>>
init with 2 2
len <<[1 2], [1 2]>> = 2
len <<[1 2], [1 2]>> = 2
init with 1 1
getitem 0 got <<1, 1>>
init with 1 1
init with 2 2
getitem 1 got <<2, 2>>
init with 2 2
log <<1, 1>>
init with 0.0 0.0
log <<2, 2>>
init with 0.693147 0.693147
--- np.log(x) with len --- [<<0.0, 0.0>> <<0.693147, 0.693147>>]
length メソッドがない場合Two
は、問題なく動作します。
>>> del Two.__len__
>>> print("--- np.log(x) without len ---", np.log(x), "\n")
log <<[1 2], [1 2]>>
init with [ 0. 0.69314718] [ 0. 0.693147]
--- np.log(x) without len --- <<[ 0. 0.693147], [ 0. 0.693147]>>
要件 (getitem、log、len) を満たすクラスを作成するにはどうすればよいですか? subclassing について調査ndarray
しましたが、それは価値があるよりも複雑なようです。
また、NumPy のソース コードで がアクセスされている場所が見つからなかった x.__len__
ので、それも興味があります。
編集:私は、Python 2.7.11 および NumPy 1.11.0 で miniconda2 を使用しています。