Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generating banded matrices using numpy

I'm using the following piece of code to create a banded matrix from a generator g:

def banded(g, N):
    """Creates a `g` generated banded matrix with 'N' rows"""
    n=len(g)
    T = np.zeros((N,N+n-1))
    for x in range(N):
        T[x][x:x+n]=g
    return T

The usage is simple as:

banded([1,2,3], 3)

and it returns

[1, 2, 3, 0, 0]
[0, 1, 2, 3, 0]
[0, 0, 1, 2, 3]

It will used mostly to solve a finite difference model with a given stencil for example (-1, 1)

Is there a better way to generate that stencil? I could not find any good NumPy Function for that.

By better I mean, faster, using less memory, removing the loop from python and sending to Numpy stack. Any (or all) of those are improvements.

like image 987
Lin Avatar asked Sep 23 '18 07:09

Lin


People also ask

How do you create a NumPy matrix?

We can create a matrix in Numpy using functions like array(), ndarray() or matrix(). Matrix function by default creates a specialized 2D array from the given input. The input should be in the form of a string or an array object-like.

What is banded matrix in FEM?

In mathematics, particularly matrix theory, a band matrix or banded matrix is a sparse matrix whose non-zero entries are confined to a diagonal band, comprising the main diagonal and zero or more diagonals on either side.

How do you create a lower triangular matrix in python?

To create an array with zero above the main diagonal forming a lower triangular matrix, use the numpy. tri() method in Python Numpy. The 1st parameter is the number of rows in the array. The 2nd parameter is the number of columns in the array.


1 Answers

Here's one with np.lib.stride_tricks.as_strided to give us a 2D view into a zeros padded 1D version of the input and as such pretty memory efficient and hence performant too. This trick had been explored numerous times - 1,2.

Thus, the implementation would be -

def sliding_windows(a, W):
    a = np.asarray(a)
    p = np.zeros(W-1,dtype=a.dtype)
    b = np.concatenate((p,a,p))
    s = b.strides[0]
    strided = np.lib.stride_tricks.as_strided
    return strided(b[W-1:], shape=(W,len(a)+W-1), strides=(-s,s))

Sample runs -

In [99]: a = [1,2,3]

In [100]: sliding_windows(a, W=3)
Out[100]: 
array([[1, 2, 3, 0, 0],
       [0, 1, 2, 3, 0],
       [0, 0, 1, 2, 3]])

In [101]: a = [1,2,3,4,5]

In [102]: sliding_windows(a, W=3)
Out[102]: 
array([[1, 2, 3, 4, 5, 0, 0],
       [0, 1, 2, 3, 4, 5, 0],
       [0, 0, 1, 2, 3, 4, 5]])

With the same philosophy, but less messier version, we can also leverage np.lib.stride_tricks.as_strided based scikit-image's view_as_windows to get sliding windows. More info on use of as_strided based view_as_windows.

from skimage.util.shape import view_as_windows

def sliding_windows_vw(a, W):
    a = np.asarray(a)
    p = np.zeros(W-1,dtype=a.dtype)
    b = np.concatenate((p,a,p))
    return view_as_windows(b,len(a)+W-1)[::-1]
like image 119
Divakar Avatar answered Oct 02 '22 12:10

Divakar