我可以将自己的Python类与numpy或其他矩阵库一起使用吗?

10 投票
5 回答
10581 浏览
提问于 2025-04-16 15:16

我想用一个Python类来进行矩阵运算,类里面的元素是简单的伽罗瓦域实现。这个类实现了必要的__add____mul____sub__等方法。

起初,我以为可以通过numpy数组来实现,使用dtype参数,但根据dtype文档,似乎dtype不能是任意的Python类。例如,我有一个类Galois,它进行模2运算:

>>> from galois import Galois
>>> Galois(1) + Galois(0)
Galois(1)
>>> Galois(1) + Galois(1)
Galois(0)

我可以尝试在numpy中使用这个:

>>> import numpy as np
>>> a = np.identity(4, Galois)
>>> a
array([[1, 0, 0, 0],
       [0, 1, 0, 0],
       [0, 0, 1, 0],
       [0, 0, 0, 1]], dtype=object)

但是如果我对矩阵进行运算,元素就不会按照我类中的方法来处理:

>>> b = np.identity(4, Galois)
>>> a+b
array([[2, 0, 0, 0],
       [0, 2, 0, 0],
       [0, 0, 2, 0],
       [0, 0, 0, 2]], dtype=object)

有没有办法让这个在numpy中工作呢?

有没有其他的Python矩阵库可以对任意数字类进行矩阵运算(包括求逆)呢?

更新

感谢到目前为止的回答。但我还是无法像我希望的那样真正使用它。加法和乘法似乎没问题,但矩阵求逆就不行了。例如,我们试着从AES逆S盒仿射变换矩阵中获取正向S盒仿射变换矩阵

class Galois(object):
    MODULO = 2

    def __init__(self, val):
        self.val = int(val) % self.MODULO

    def __add__(self, val):
        return self.__class__((self.val + int(val)) % self.MODULO)
    def __sub__(self, val):
        return self.__class__((self.val - int(val)) % self.MODULO)
    def __mul__(self, val):
        return self.__class__((self.val * int(val)) % self.MODULO)
    def __int__(self):
        return self.val
    def __repr__(self):
        return "%s(%d)" % (self.__class__.__name__, self.val)
    def __float__(self):
        return float(self.val)

if __name__ == "__main__":
    import numpy as np

    Gv = np.vectorize(Galois)

    a = Gv(np.identity(8)) + Gv(np.eye(8,8,-1)) + Gv(np.eye(8,8,-2)) + Gv(np.eye(8,8,-3)) + Gv(np.eye(8,8,-4)) + Gv(np.eye(8,8,4)) + Gv(np.eye(8,8,5)) + Gv(np.eye(8,8,6)) + Gv(np.eye(8,8,7))
    print np.matrix(a)
    print np.matrix(a).I

结果是:

[[Galois(1) Galois(0) Galois(0) Galois(0) Galois(1) Galois(1) Galois(1)
  Galois(1)]
 [Galois(1) Galois(1) Galois(0) Galois(0) Galois(0) Galois(1) Galois(1)
  Galois(1)]
 [Galois(1) Galois(1) Galois(1) Galois(0) Galois(0) Galois(0) Galois(1)
  Galois(1)]
 [Galois(1) Galois(1) Galois(1) Galois(1) Galois(0) Galois(0) Galois(0)
  Galois(1)]
 [Galois(1) Galois(1) Galois(1) Galois(1) Galois(1) Galois(0) Galois(0)
  Galois(0)]
 [Galois(0) Galois(1) Galois(1) Galois(1) Galois(1) Galois(1) Galois(0)
  Galois(0)]
 [Galois(0) Galois(0) Galois(1) Galois(1) Galois(1) Galois(1) Galois(1)
  Galois(0)]
 [Galois(0) Galois(0) Galois(0) Galois(1) Galois(1) Galois(1) Galois(1)
  Galois(1)]]
[[ 0.4  0.4 -0.6  0.4  0.4 -0.6  0.4 -0.6]
 [-0.6  0.4  0.4 -0.6  0.4  0.4 -0.6  0.4]
 [ 0.4 -0.6  0.4  0.4 -0.6  0.4  0.4 -0.6]
 [-0.6  0.4 -0.6  0.4  0.4 -0.6  0.4  0.4]
 [ 0.4 -0.6  0.4 -0.6  0.4  0.4 -0.6  0.4]
 [ 0.4  0.4 -0.6  0.4 -0.6  0.4  0.4 -0.6]
 [-0.6  0.4  0.4 -0.6  0.4 -0.6  0.4  0.4]
 [ 0.4 -0.6  0.4  0.4 -0.6  0.4 -0.6  0.4]]

结果并不是我希望的。看起来在进行矩阵求逆时,numpy只是把矩阵转换成浮点数,然后用普通的实数进行求逆。

5 个回答

2

关于你提到的矩阵求逆的问题:用NumPy来对伽罗瓦域的矩阵进行求逆是行不通的。NumPy实际上把矩阵求逆的工作交给了一个叫做LAPACK的库,这个库是用Fortran语言写的线性代数库。LAPACK完全不知道Python的类和操作符,所以它无法调用你自己定义的Galois类的方法。此外,他们的矩阵求逆算法使用了一些比较操作符(比如<>),而这些在伽罗瓦域的元素上是没有意义的。

所以你有两个选择:要么自己实现矩阵求逆,要么使用现成的实现。例如,SymPy对伽罗瓦域有一些有限的支持。PARI/GP则支持伽罗瓦域,并且有一些Python的绑定

2

这里是如何用另一个数组来创建和初始化一个Numpy对象数组的方法:

import numpy as np

class G:
    def __init__(self, x):
        self.x = x

I = np.identity(5)
Gv = np.vectorize(G)
GG = Gv(I)

print GG[0,0].x
print GG[0,1].x
8

你可以把 object 作为 dtype,这样就可以让你放入任意的 Python 对象。我觉得没有办法让 numpy 数组只接受某一种特定类型的 Python 对象。

撰写回答