user1482714 - 11 months ago 65

Python Question

I want to create a large (say 10^5 x 10^5) sparse circulant matrix in Python. It has 4 elements per row at positions

`[i,i+1], [i,i+2], [i,i+N-2], [i,i+N-1]`

`[10^5,10^5]=[0,0], [10^5+1,10^5+1]=[1,1]`

I can create the matrix with numpy

`import numpy as np`

def Bc(i, boundary):

"""(int, int) -> int

Checks boundary conditions on index

"""

if i > boundary - 1:

return i - boundary

elif i < 0:

return boundary + i

else:

return i

N = 100

diffMat = np.zeros([N, N])

for i in np.arange(0, N, 1):

diffMat[i, [Bc(i+1, N), Bc(i+2, N), Bc(i+2+(N-5)+1, N), Bc(i+2+(N-5)+2, N)]] = [2.0/3, -1.0/12, 1.0/12, -2.0/3]

However, this is quite slow and for large

`N`

I know how to do it in Mathematica, where one can use SparseArray and index patterns - is there something similar here?

Answer Source

To create a *dense* circulant matrix, you can use `scipy.linalg.circulant`

. For example,

```
In [210]: from scipy.linalg import circulant
In [211]: N = 7
In [212]: vals = np.array([2.0/3, -1.0/12, 1.0/12, -2.0/3])
In [213]: offsets = np.array([1, 2, N-2, N-1])
In [214]: col0 = np.zeros(N)
In [215]: col0[offsets] = -vals
In [216]: c = circulant(col0)
In [217]: c
Out[217]:
array([[ 0. , 0.6667, -0.0833, 0. , 0. , 0.0833, -0.6667],
[-0.6667, 0. , 0.6667, -0.0833, 0. , 0. , 0.0833],
[ 0.0833, -0.6667, 0. , 0.6667, -0.0833, 0. , 0. ],
[ 0. , 0.0833, -0.6667, 0. , 0.6667, -0.0833, 0. ],
[ 0. , 0. , 0.0833, -0.6667, 0. , 0.6667, -0.0833],
[-0.0833, 0. , 0. , 0.0833, -0.6667, 0. , 0.6667],
[ 0.6667, -0.0833, 0. , 0. , 0.0833, -0.6667, 0. ]])
```

As you point out, for large `N`

, that requires a lot of memory and most of the values are zero. To create a scipy sparse matrix, you can use `scipy.sparse.diags`

. We have to create offsets (and corresponding values) for the diagonals above and below the main diagonal:

```
In [218]: from scipy import sparse
In [219]: N = 7
In [220]: vals = np.array([2.0/3, -1.0/12, 1.0/12, -2.0/3])
In [221]: offsets = np.array([1, 2, N-2, N-1])
In [222]: dupvals = np.concatenate((vals, vals[::-1]))
In [223]: dupoffsets = np.concatenate((offsets, -offsets))
In [224]: a = sparse.diags(dupvals, dupoffsets, shape=(N, N))
In [225]: a.toarray()
Out[225]:
array([[ 0. , 0.6667, -0.0833, 0. , 0. , 0.0833, -0.6667],
[-0.6667, 0. , 0.6667, -0.0833, 0. , 0. , 0.0833],
[ 0.0833, -0.6667, 0. , 0.6667, -0.0833, 0. , 0. ],
[ 0. , 0.0833, -0.6667, 0. , 0.6667, -0.0833, 0. ],
[ 0. , 0. , 0.0833, -0.6667, 0. , 0.6667, -0.0833],
[-0.0833, 0. , 0. , 0.0833, -0.6667, 0. , 0.6667],
[ 0.6667, -0.0833, 0. , 0. , 0.0833, -0.6667, 0. ]])
```

The matrix is stored in the "diagonal" format:

```
In [226]: a
Out[226]:
<7x7 sparse matrix of type '<class 'numpy.float64'>'
with 28 stored elements (8 diagonals) in DIAgonal format>
```

You can use the conversion methods of the sparse matrix to convert it to a different sparse format. For example, the following results in a matrix in CSR format:

```
In [227]: a.tocsr()
Out[227]:
<7x7 sparse matrix of type '<class 'numpy.float64'>'
with 28 stored elements in Compressed Sparse Row format>
```