用于提供不同线性代数后端的架构

时间:2011-03-19 21:48:43

标签: python architecture linear-algebra backend metaclass

我正在用Python原型化一个新系统;功能主要是数字。

一个重要的要求是能够使用不同的线性代数后端:从单个用户实现到通用库,如Numpy。线性代数实现(即后端)必须独立于接口。

我最初的架构尝试如下:

(1)定义系统接口

>>> v1 = Vector([1,2,3])
>>> v2 = Vector([4,5,6])
>>> print v1 * v2
>>> # prints "Vector([4, 10, 18])"

(2)实现允许独立于后端使用该接口的代码

# this example uses numpy as the back-end, but I mean
# to do this for a general back-end
import numpy 
def numpy_array(*args): # creates a numpy array from the arguments
    return numpy.array(*args)

class VectorBase(type):
    def __init__(cls, name, bases, attrs):
        engine = attrs.pop("engine", None)
        if not engine:
            raise RuntimeError("you need to specify an engine")
        # this implementation would change depending on `engine`
        def new(cls, *args):
            return numpy_array(*args)   
        setattr(cls, "new", classmethod(new))

class Vector(object):   
    __metaclass__ = VectorBase        
    # I could change this at run time
    # and offer alternative back-ends
    engine = "numpy"  
    @classmethod
    def create(cls, v):
        nv = cls()
        nv._v = v
        return nv    
    def __init__(self, *args):  
        self._v = None
        if args:
            self._v = self.new(*args)
    def __repr__(self):
        l = [item for item in self._v]
        return "Vector(%s)" % repr(l)
    def __mul__(self, other):
        try:
            return Vector.create(self._v * other._v)
        except AttributeError:
            return Vector.create(self._v * other)
    def __rmul__(self, other):
        return self.__mul__(other)

这个简单的例子如下:Vector类保留对后端(在示例中为numpy.ndarray)所做的向量实例的引用;所有算术调用都由接口实现,但它们的评估推迟到后端。

在实践中,接口会重载所有适当的运算符并延迟到后端(示例仅显示__mul____rmul__,但您可以遵循相同的操作来执行每个操作)。

我愿意放弃一些性能来换取可定制性。即使在我的示例有效的情况下,它也感觉不对 - 我会用很多构造函数调用来削弱后端!这需要一个不同的metaclass实现,允许更好的呼叫延迟。

那么,您如何推荐我实现此功能?我需要强调保持所有系统的Vector实例均匀且独立于线性代数后端的重要性。

3 个答案:

答案 0 :(得分:6)

您应该查看PEP-3141和标准的lib模块ABCMeta

有关如何使用ABCMeta的详细说明,总是有帮助的PyMOTW有一个很好的写作。

答案 1 :(得分:3)

为什么不简单地创建一个类似于Vector的“虚拟”类(AbstractVector),并为每个实现创建不同的子类?

可以通过Vector = NumPyVector或类似的东西来选择引擎。

答案 2 :(得分:2)

仅供参考,您可以轻松配置和构建NumPy,以使用英特尔的数学核心库或AMD的核心数学库,而不是通常的ATLAS + LAPACK。这就像创建blas_libs文件一样简单,lapack_libslibrary_dirsinclude_dirssetup.py变量设置得恰当。 (为MKL和ACML设置这些选项的详细信息很容易实现。)将它放在{{1}}脚本旁边并照常构建。

要在这些标准线性代数库之间切换,您可以为每个库构建一个不同的NumPy实例,并使用site.cfg来管理它们。

我知道这并不能为您提供使用自己的自定义数学库所需的灵活性,但我只是想把它扔出去。虽然我没有调查它,但我想你也可以让NumPy构建一个自定义库,而不是构建自己的前端所需的工作量,特别是如果你想保留广泛的功能NumPy / SciPy大厦。