Can I use my own Python class with numpy or some other matrix library?

◇◆丶佛笑我妖孽 提交于 2020-01-01 08:47:02

问题


I'd like to be able to do matrix operations using a Python class as the elements—in this case, a simple Galois field implementation. It implements the necessary __add__, __mul__, __sub__ etc.

At first, I thought this should be possible with numpy arrays, using the dtype parameter, but from the dtype documentation, it seems that dtype can't be an arbitrary Python class. For example, I have a class Galois which does operations modulo 2:

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

I can try to use this in 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)

But if I do operations on the matrices, the elements aren't following the methods of my class:

>>> 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)

Is there any way to make this work with numpy?

Is there any other Python matrix library that can do matrix operations (including inversion) on an arbitrary number-like class?

Update

Thanks for the answers so far. But I'm still not able to really use it as I hoped. Adds and multiplies seem good, but not matrix inversion. For example, let's try to get the AES inverse S-box affine transform matrix from the forward S-box affine transform matrix.

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

The result:

[[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]]

Not the result I hoped for. It seems that for the matrix inversion, numpy just converts the matrix to floats, then does the inversion with plain real numbers.


回答1:


You can use object as the dtype, which will allow arbitrary Python objects. I don't think there's any way of specializing a numpy array to accept only one particular class of Python object.




回答2:


Here is how you can create and initialize a Numpy object array with another array:

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



回答3:


Have you checked out sage and specifically galois_group.

It seems that you are reinventing the wheel. But if you insist doing that you may consider to subclass ndarray.




回答4:


Regarding your update on matrix inversion: Using NumPy matrix inversion to invert matrices over Galois fields won't work. NumPy delegates the task of actually inverting the matrix to LAPACK, which is a linear algebra library written in Fortran. LAPACK is of course completely unaware of Python classes and operators, and will never be able to call methods on your Galois class. Additionally, their matrix inversion algorithm makes use of comparison operators (like < and >), which do not make sense for elements of Galois fields.

So your options are either to implement the matrix inversion yourself or to use one of the available implementations. For example, SymPy has a limited support for Galois fields. PARI/GP has support for Galois fields and some Python bindings.




回答5:


I am not aware of a way to have matrix elements behave according to an arbitrary Python class. It is however possible to globally change the behaviour of certain operations, as the following examples show,

import numpy as np
from numpy import set_numeric_ops

from numpy import poly1d as poly
from numpy import identity as idt

def gfadd(x,y):
    return np.add(x,y) % 2

set_numeric_ops(add=gfadd)

a = idt(4,np.int)
print a+a

produces,

[[0 0 0 0]
 [0 0 0 0]
 [0 0 0 0]
 [0 0 0 0]]

and,

p = poly([1,0,1])
print p+p

gives,

0

However, you probably want to subclass ndarray.



来源:https://stackoverflow.com/questions/5578172/can-i-use-my-own-python-class-with-numpy-or-some-other-matrix-library

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!