Numpy matrix inversion with objects - python

I'm using the gf256 library to do galois field math, and I have it in a numpy matrix. Though when calling np.linalg.inv() with it, it throws an error.
That's the summary, here's the details:
import numpy as np
from gf256 import GF256 as gf
npgf = np.vectorize(gf)
arr = np.identity(4, np.uint8) * 10
gfarr = npgf(arr)
After all this, gfarr looks like this
array([[GF256(0b00001010), GF256(0b00000000), GF256(0b00000000),
GF256(0b00000000)],
[GF256(0b00000000), GF256(0b00001010), GF256(0b00000000),
GF256(0b00000000)],
[GF256(0b00000000), GF256(0b00000000), GF256(0b00001010),
GF256(0b00000000)],
[GF256(0b00000000), GF256(0b00000000), GF256(0b00000000),
GF256(0b00001010)]], dtype=object)
And np.linalg.inv(gfarr) throws this error
Traceback (most recent call last):
File "<pyshell#152>", line 1, in <module>
np.linalg.inv(gfarr)
File "[python3.6]\lib\site-packages\numpy\linalg\linalg.py", line 528, in inv
ainv = _umath_linalg.inv(a, signature=signature, extobj=extobj)
TypeError: No loop matching the specified signature and casting
was found for ufunc inv
The matrix is definitely invertable, and the GF256 class supports all the usual operators. Is it possible to make this work with numpy?

np.linalg.inv will invoke a BLAS/LAPACK implementation of matrix inversion using floats, but you need to use Galois field arithmetic in the matrix inversion process. To do this, the NumPy array will need to intercept or override the call to np.linalg.inv in __array_function__(). The matrix inversion of A can be accomplished using Gaussian elimination on [A | I] over the Galois field, which yields [I | A^-1].
I had a similar use case, so I created a Python package called galois that extends NumPy arrays over Galois fields. It replaces the NumPy ufuncs with JIT compiled ufuncs using Numba. This means the array arithmetic is as fast, or nearly as fast, as normal NumPy arithmetic. See this performance comparison.
It also supports linear algebra and overrides the relevant np.linalg functions. So the matrix inversion you're looking for works out of the box. Here's an example using your matrix.
In [1]: import numpy as np
In [2]: import galois
In [3]: GF = galois.GF(2**8)
In [4]: print(GF.properties)
GF(2^8):
characteristic: 2
degree: 8
order: 256
irreducible_poly: x^8 + x^4 + x^3 + x^2 + 1
is_primitive_poly: True
primitive_element: x
In [5]: A = GF.Identity(4) * GF(10); A
Out[5]:
GF([[10, 0, 0, 0],
[ 0, 10, 0, 0],
[ 0, 0, 10, 0],
[ 0, 0, 0, 10]], order=2^8)
In [6]: A_inv = np.linalg.inv(A); A_inv
Out[6]:
GF([[221, 0, 0, 0],
[ 0, 221, 0, 0],
[ 0, 0, 221, 0],
[ 0, 0, 0, 221]], order=2^8)
In [7]: A # A_inv
Out[7]:
GF([[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]], order=2^8)

Related

Python-How to multiply matrix with symbols and 0s

I am brand new to python, but is there any way to multiply matrices with both 0's and symbols? For example, see below:
import sympy as sym
import numpy as np
teams=np.matrix([[1,2],[3,4]])
teams=teams-1
n=4
x,a,b=sym.symbols('x a b')
X=np.empty((n,n), dtype=object)
Y=np.empty((n,n), dtype=object)
Z=np.empty((n,n), dtype=object)
for i in range(n):
for j in range(n):
if j==i:
X[i,j]=x
elif ([i,j] in teams.tolist()):
Y[i,j]=a
elif ([j,i] in teams.tolist()):
Y[i,j]=a
else:
Z[i,j]=b
for i in range(n):
for j in range(n):
if X[i,j]==None:
X[i,j]=0
if Y[i,j]==None:
Y[i,j]=0
if Z[i,j]==None:
Z[i,j]=0
print(np.matmul(X,Y))
TypeError Traceback (most recent call last)
<ipython-input-189-00b753462a2d> in <module>
2 print(Y)
3 print(Z)
----> 4 print(np.matmul(X,Y))
TypeError: ufunc 'matmul' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
I know why it is messing up, I am trying to multiply a symbol by a number, but I was wondering if there was anyway to make this recognize that a symbol times 0 is just zero and should be disregarded if being added to another symbol.
The problem isn't specifically with the symbols, but with the object dtype. matmul doesn't (or didn't) work with object dtype arrays. The fast version uses BLAS library functions, which only work with C numeric types - float and integers. np.dot does have a slower branch that does work with non-numeric dtypes.
In a isympy session:
In [4]: X
Out[4]:
array([[x, 0, 0, 0],
[0, x, 0, 0],
[0, 0, x, 0],
[0, 0, 0, x]], dtype=object)
In [5]: Y
Out[5]:
array([[0, a, 0, 0],
[a, 0, 0, 0],
[0, 0, 0, a],
[0, 0, a, 0]], dtype=object)
In [6]: np.dot(X,Y)
Out[6]:
array([[0, a*x, 0, 0],
[a*x, 0, 0, 0],
[0, 0, 0, a*x],
[0, 0, a*x, 0]], dtype=object)
BUT, matmul does work for me. I wonder if that's because of my numpy version?
In [7]: np.matmul(X,Y)
Out[7]:
array([[0, a*x, 0, 0],
[a*x, 0, 0, 0],
[0, 0, 0, a*x],
[0, 0, a*x, 0]], dtype=object)
In [8]: np.__version__
Out[8]: '1.17.4'
As a general rule mixing sympy and numpy is not a good idea. numpy arrays containing symbols are necessarily object dtype. Math on object dtype depends on delegating the action to methods. The result is hit-or-miss. Multiplication and addition may work (x+x), but np.sin does not, because x.sin() fails. It's best to use sympy.lambdify if you want to use sympy expressions in numpy. Otherwise, try to use pure sympy.
In [12]: X*X
Out[12]:
array([[x**2, 0, 0, 0],
[0, x**2, 0, 0],
[0, 0, x**2, 0],
[0, 0, 0, x**2]], dtype=object)
In [13]: np.sin(X)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
AttributeError: 'Symbol' object has no attribute 'sin'
===
From the numpy 1.17.0 release notes
Support of object arrays in matmul¶
It is now possible to use matmul (or the # operator) with object arrays. For instance, it is now possible to do:
from fractions import Fraction
a = np.array([[Fraction(1, 2), Fraction(1, 3)], [Fraction(1, 3), Fraction(1, 2)]])
b = a # a
Whenever you are working with symbolic math, you should leave out numpy and keep everything inside sympy. Numpy doesn't understand about sympy's symbols. You can be lucky a few times with multiplying by zero, but it doesn't make much sense in general. Numpy works with arrays of numbers, preferably everything of the same type.
However, you can use lambdify to bridge the gap and convert sympy expressions to be used by numpy.
Here is your code with sympy's matrices:
import sympy as sym
teams = sym.Matrix([[1, 2], [3, 4]])
teams = teams - sym.ones(2, 2)
n = 4
x, a, b = sym.symbols('x a b')
X = sym.zeros(n, n)
Y = sym.zeros(n, n)
Z = sym.zeros(n, n)
for i in range(n):
for j in range(n):
if j == i:
X[i, j] = x
elif [i, j] in teams.tolist() or [j, i] in teams.tolist():
Y[i, j] = a
else:
Z[i, j] = b
for i in range(n):
for j in range(n):
if X[i, j] is None:
X[i, j] = 0
if Y[i, j] is None:
Y[i, j] = 0
if Z[i, j] is None:
Z[i, j] = 0
print(X * Y)
Result:
Matrix([[0, a*x, 0, 0],
[a*x, 0, 0, 0],
[0, 0, 0, a*x],
[0, 0, a*x, 0]])
I tested your code with print(np.dot(X,Y)) instead of print(np.matmul(X,Y)) and it worked. According to the documentation np.matmul is preferred over np.dot for matrix multiplication, but I wasn't able to figure out how to do it using np.matmul. I tried np.matmul(X, Y, casting='unsafe'), but the same error resulted. I don't think the error is caused by adding 0 or multiplying by 0, sympy is able to do simplifications.
E.g.
x = sym.symbols('x')
print(x + 0)
print(x*0)
print(3*x + 5*x)
returns just as expected x, 0 and x*8.
Hopefully this helps you out.

Coefficients of Charpoly using Sympy in Python

I am new to using the library Sympy. I am need to extract all coefficients of the characteristic polynomial to be used later.
For example, my code is:
import sympy as sp
M = sp.Matrix([[0, 0, 0, 1, 0, 1], [0, 0, 0, 0, 1, 0], [0, 1, 0, 1, 0, -1], [1, 0, -1, 0, 1, 0], [0, 0, 0, 1, 0, 0], [-1, 0, 1, 0, 0, 0]])
lamda = symbols('lamda')
p = M.charpoly(lamda)
print(p)
print(p.coeffs())
which gives output:
PurePoly(lamda**6 + lamda**4 - lamda**2, lamda, domain='ZZ')
[1, 1, -1]
However, I need [1, 0, 1, 0, 1, 0, 0], which includes the zero coefficients of the lamda too the exponents 4, 3, 1, and 0, terms. I would normally use a for loop to iterate over the equation to see which terms are missing so a zero can be inserted into the appropriate spot in the array of coefficients. However, when I attempted to do so, I received an error saying PurePoly type doesn't support indexing. So, I was wondering if anyone knows how to make sympy include the zeros or a way to do it myself? I need will eventually have to incorporate this code into a loop for lots of matrices so I can't manually do it.
Thanks.
When I have questions like this I hope for some sort of intelligent naming of methods for objects and look through the directory of the object:
>>> print([w for w in dir(p) if 'coeff' in w])
['all_coeffs', 'as_coeff_Add', 'as_coeff_Mul', ...]
That all_coeffs is the one you want:
>>> help(p.all_coeffs)
Help on method all_coeffs in module sympy.polys.polytools:
all_coeffs(f) method of sympy.polys.polytools.PurePoly instance
Returns all coefficients from a univariate polynomial ``f``.
>>> p.all_coeffs()
[1,0,1,0,−1,0,0]

How to fold/accumulate a numpy matrix product (dot)?

With using python library numpy, it is possible to use the function cumprod to evaluate cumulative products, e.g.
a = np.array([1,2,3,4,2])
np.cumprod(a)
gives
array([ 1, 2, 6, 24, 48])
It is indeed possible to apply this function only along one axis.
I would like to do the same with matrices (represented as numpy arrays), e.g. if I have
S0 = np.array([[1, 0], [0, 1]])
Sx = np.array([[0, 1], [1, 0]])
Sy = np.array([[0, -1j], [1j, 0]])
Sz = np.array([[1, 0], [0, -1]])
and
b = np.array([S0, Sx, Sy, Sz])
then I would like to have a cumprod-like function which gives
np.array([S0, S0.dot(Sx), S0.dot(Sx).dot(Sy), S0.dot(Sx).dot(Sy).dot(Sz)])
(This is a simple example, in reality I have potentially large matrices evaluated over n-dimensional meshgrids, so I seek for the most simple and efficient way to evaluate this thing.)
In e.g. Mathematica I would use
FoldList[Dot, IdentityMatrix[2], {S0, Sx, Sy, Sz}]
so I searched for a fold function, and all I found is an accumulate method on numpy.ufuncs. To be honest, I know that I am probably doomed because an attempt at
np.core.umath_tests.matrix_multiply.accumulate(np.array([pauli_0, pauli_x, pauli_y, pauli_z]))
as mentioned in a numpy mailing list yields the error
Reduction not defined on ufunc with signature
Do you have an idea how to (efficiently) do this kind of calculation ?
Thanks in advance.
As food for thought, here are 3 ways of evaluating the 3 sequential dot products:
With the normal Python reduce (which could also be written as a loop)
In [118]: reduce(np.dot,[S0,Sx,Sy,Sz])
array([[ 0.+1.j, 0.+0.j],
[ 0.+0.j, 0.+1.j]])
The einsum equivalent
In [119]: np.einsum('ij,jk,kl,lm',S0,Sx,Sy,Sz)
The einsum index expression looks like a sequence of operations, but it is actually evaluated as a 5d product with summation on 3 axes. In the C code this is done with an nditer and strides, but the effect is as follows:
In [120]: np.sum(S0[:,:,None,None,None] * Sx[None,:,:,None,None] *
Sy[None,None,:,:,None] * Sz[None,None,None,:,:],(1,2,3))
In [127]: np.prod([S0[:,:,None,None,None], Sx[None,:,:,None,None],
Sy[None,None,:,:,None], Sz[None,None,None,:,:]]).sum((1,2,3))
A while back while creating a patch from np.einsum I translated that C code to Python, and also wrote a Cython sum-of-products function(s). This code is on github at
https://github.com/hpaulj/numpy-einsum
einsum_py.py is the Python einsum, with some useful debugging output
sop.pyx is the Cython code, which is compiled to sop.so.
Here's how it could be used for part of your problem. I'm skipping the Sy array since my sop is not coded for complex numbers (but that could be changed).
import numpy as np
import sop
import einsum_py
S0 = np.array([[1., 0], [0, 1]])
Sx = np.array([[0., 1], [1, 0]])
Sz = np.array([[1., 0], [0, -1]])
print np.einsum('ij,jk,kl', S0, Sx, Sz)
# [[ 0. -1.] [ 1. 0.]]
# same thing, but with parsing information
einsum_py.myeinsum('ij,jk,kl', S0, Sx, Sz, debug=True)
"""
{'max_label': 108, 'min_label': 105, 'nop': 3,
'shapes': [(2, 2), (2, 2), (2, 2)],
'strides': [(16, 8), (16, 8), (16, 8)],
'ndim_broadcast': 0, 'ndims': [2, 2, 2], 'num_labels': 4,
....
op_axes [[0, -1, 1, -1], [-1, -1, 0, 1], [-1, 1, -1, 0], [0, 1, -1, -1]]
"""
# take op_axes (for np.nditer) from this debug output
op_axes = [[0, -1, 1, -1], [-1, -1, 0, 1], [-1, 1, -1, 0], [0, 1, -1, -1]]
w = sop.sum_product_cy3([S0,Sx,Sz], op_axes)
print w
As written sum_product_cy3 cannot take an arbitrary number of ops. Plus the iteration space increases with each op and index. But I can imagine calling it repeatedly, either at the Cython level, or from Python. I think it has potential for being faster than repeat(dot...) for lots of small arrays.
A condensed version of the Cython code is:
def sum_product_cy3(ops, op_axes, order='K'):
#(arr, axis=None, out=None):
cdef np.ndarray[double] x, y, z, w
cdef int size, nop
nop = len(ops)
ops.append(None)
flags = ['reduce_ok','buffered', 'external_loop'...]
op_flags = [['readonly']]*nop + [['allocate','readwrite']]
it = np.nditer(ops, flags, op_flags, op_axes=op_axes, order=order)
it.operands[nop][...] = 0
it.reset()
for x, y, z, w in it:
for i in range(x.shape[0]):
w[i] = w[i] + x[i] * y[i] * z[i]
return it.operands[nop]

Why does SymPy give me the wrong answer when I row-reduce a symbolic matrix?

If I ask SymPy to row-reduce the singular matrix
nu = Symbol('nu')
lamb = Symbol('lambda')
A3 = Matrix([[-3*nu, 1, 0, 0],
[3*nu, -2*nu-1, 2, 0],
[0, 2*nu, (-1 * nu) - lamb - 2, 3],
[0, 0, nu + lamb, -3]])
print A3.rref()
then it returns the identity matrix
(Matrix([
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]]), [0, 1, 2, 3])
which it shouldn't do, since the matrix is singular. Why is SymPy giving me the wrong answer and how can I get it to give me the right answer?
I know SymPy knows the matrix is singular, because when I ask for A3.inv(), it gives
raise ValueError("Matrix det == 0; not invertible.")
Furthermore, when I remove lamb from the matrix (equivalent to setting lamb = 0), SymPy gives the correct answer:
(Matrix([
[1, 0, 0, -1/nu**3],
[0, 1, 0, -3/nu**2],
[0, 0, 1, -3/nu],
[0, 0, 0, 0]]), [0, 1, 2])
which leads me to believe that this problem only happens with more than one variable.
EDIT: Interestingly, I just got the correct answer when I pass rref() the argument "simplify=True". I still have no idea why that is though.
The rref algorithm fundamentally requires the ability to tell if the elements of the matrix are identically zero. In SymPy, the simplify=True option instructs SymPy to simplify the entries first at the relevant stage of the algorithm. With symbolic entries, this is necessary, as you can easily have symbolic expressions that are identically zero but which don't simplify to such automatically, like x*(x - 1) - x**2 + x. The option is off by default because in general such simplification can be expensive, through this can be controlled by passing in a less general simplify function than simplify (for rational functions, use cancel). The defaults here could probably be smarter.

Is there a "bounding box" function (slice with non-zero values) for a ndarray in NumPy?

I am dealing with arrays created via numpy.array(), and I need to draw points on a canvas simulating an image. Since there is a lot of zero values around the central part of the array which contains the meaningful data, I would like to "trim" the array, erasing columns that only contain zeros and rows that only contain zeros.
So, I would like to know of some native numpy function or even a code snippet to "trim" or find a "bounding box" to slice only the data-containing part of the array.
(since it is a conceptual question, I did not put any code, sorry if I should, I'm very fresh to posting at SO.)
Thanks for reading
This should do it:
from numpy import array, argwhere
A = array([[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0],
[0, 0, 1, 1, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0]])
B = argwhere(A)
(ystart, xstart), (ystop, xstop) = B.min(0), B.max(0) + 1
Atrim = A[ystart:ystop, xstart:xstop]
The code below, from this answer runs fastest in my tests:
def bbox2(img):
rows = np.any(img, axis=1)
cols = np.any(img, axis=0)
ymin, ymax = np.where(rows)[0][[0, -1]]
xmin, xmax = np.where(cols)[0][[0, -1]]
return img[ymin:ymax+1, xmin:xmax+1]
The accepted answer using argwhere worked but ran slower. My guess is, it's because argwhere allocates a giant output array of indices. I tested on a large 2D array (a 1024 x 1024 image, with roughly a 50x100 nonzero region).
Something like:
empty_cols = sp.all(array == 0, axis=0)
empty_rows = sp.all(array == 0, axis=1)
The resulting arrays will be 1D boolian arrays. Loop on them from both ends to find the 'bounding box'.

Categories

Resources