Архитектура для предоставления различных линейных алгебрах

Я прототипирую новую систему в Python; функциональность в основном численная.

Важным требованием является возможность использования разных линейных алгебраических back-end: от отдельных пользовательских реализаций до общих библиотек, таких как 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 однородными и независимыми от линейной алгебры.

Ответы

Ответ 1

Вы должны проверить PEP-3141 и стандартный модуль lib ABCMeta.

Подробное объяснение того, как использовать ABCMeta, всегда полезно PyMOTW иметь хорошую запись.

Ответ 2

Почему бы просто не сделать "виртуальный" класс (AbstractVector), который похож на Vector в вашем примере, и сделать для него разные подклассы для него?

Двигатель можно выбрать, выполнив Vector = NumPyVector или что-то в этом роде.

Ответ 3

Просто FYI, вы можете легко настроить и построить NumPy, чтобы использовать библиотеку ядра Intel Math или библиотеку AMD Core Math вместо обычного ATLAS + LAPACK. Это так же просто, как создание файла site.cfg с набором переменных blas_libs, lapack_libs, library_dirs и include_dirs соответственно. (Подробности для установки этих параметров для MKL и ACML легко доступны для Google.) Поместите его рядом с setup.py script и создайте, как обычно.

Чтобы переключаться между этими стандартными библиотеками линейных алгебр, вы можете создать другой экземпляр NumPy для каждого и управлять ими, например, virtualenvs.

Я знаю, что это не дает вам гибкости, необходимой для использования ваших собственных математических математических библиотек, но просто подумал, что я брошу это там. И хотя я не рассматривал это, я думаю, вы также можете получить NumPy для создания против пользовательской библиотеки с меньшими усилиями, чем потребовалось бы, чтобы создать собственный интерфейс, особенно если вы хотите сохранить расширенную функциональность здания NumPy/SciPy.