Skip to content

Matrix classes for matrices that are block-tridiagonal and sparse, and simply "block sparse". These talk together, and furthermore containts an algorithm for inversion of the block-tridiagonal version. Much faster than the numpy and scipy equivalents when a particular matrix is block tridiagonal and large enough.

Notifications You must be signed in to change notification settings

AleksBL/Block_TD_block_sparse

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

52 Commits
 
 
 
 

Repository files navigation

Block_TD_block_sparse

Matrix classes for matrices that are block-tridiagonal and zero elsewhere, and simply "block sparse" - meaning they only have a few non-zero blocks, for which the data is stored in numpy arrays here. We are somewhere between the sparse scipy-class, but with some of the speed that comes with smaller dense arrays. These types of matrices are often seen in models for electronic structure in a localised orbital basis in quantum transport calculations.

Contains inversion algorithm for a block-tridiagonal matrix as the the one found in found in e.g. "Matthew G Reuter and Judith C Hill An efficient, block-by-block algorithm for inverting a block tridiagonal, nearly block Toeplitz matrix" and "Improvements on non-equilibrium and transport Green function techniques: The next-generation TRANSIESTA" by: Nick Rübner Papior, Nicolás Lorente, Thomas Frederiksen, Alberto García, Mads Brandbyge (sorry whomever came up with the algorithm, havent found the original paper).

Inversion is a pesky O(2.4) operation, meaning it is favorable use the block-tridiagonal structure of a matrix if it happens to be structured in such a way.

Relies on numpy broadcasting meaning the block matrix can be of (K,L,N,i,j), where i,j are the actual matrix indecies.

You should just the the "Block_matrices.py"-script and put it in a folder where you want to do your calculations The basic workings are as described below, but can also do a bit more advanced stuff, like linear elementwise interpolation in the "N"-index, make matrices from the scipy sparse format, and sisl Hamiltonians.

The python package "sparse" will also be implemented at some point.

Also, see siesta_python for how to get the greens function for an electronic structure in this format

Write to abalo@dtu.dk if you want something implemented

How to do calculations

(Start of python-code)

import numpy as np
from Block_matrices import block_sparse, block_td
import matplotlib.pyplot as plt
r = np.random.random

Matrix A :

block_shape = (3,3)
vals = [r((3,5,5)), r((3,5,5)),r((3,5,5))]
inds = [[0,0],[1,2],[2,2]]
A = block_sparse(inds,vals,block_shape)

Matrix B:

block_shape = (3,3)
vals = [r((3,5,5)),r((3,5,5))]
inds = [[0,0],[2,0]]
B = block_sparse(inds,vals,block_shape)

We can do matrix multiplication with the, where only the

two last indecies count as matrix indecies, the rest uses numpy's broadcasting

C = A.BDot(B)

We can copy arrays

C2 = C.copy()
#but only tranpose them manually, which modifies all the values
C2.do_transposition()
D = A.BDot(C2)


for i in range(2):
    D = B.BDot(D)
    D.do_transposition()

We can to a trace

print(D.Tr())

We can do a trace with a product of itself (and of course any other matrix with the same block-shape)

print(D.TrProd(D))

the block_sparse class can only do matrix-multiplication and traces (and maybe more more if anybody asks). Matrices should be have a lot of zeros for this to be efficient, as the python-for loops will become apparent if there are many nonzero entries

The "Block-tridiagonal" part

Lets make a "A0-A1-A1-A2-A3" Block-tridiagonal matrix

s1 = 2
s2 = 4
a0 = r((3,s1,s1))
a1 = r((3,s2,s2))
a2 = r((3,s1,s1))
a3 = r((3,s1,s1))

Al = [a0, a1, a2, a3];                                      Ia = [0,1,1,2,3] # Notice we can place the same block several places
Bl = [r((3,s2,s1)),r((3,s2,s2)),r((3,s1,s2)),r((3,s1,s1))]; Ib = [0,1,2,3]
Cl = [r((3,s1,s2)),r((3,s2,s2)),r((3,s2,s1)),r((3,s1,s1))]; Ic = [0,1,2,3]


BTD = block_td(Al,Bl,Cl,Ia,Ib,Ic)
iBTD = BTD.Invert()

Taking the trace of the their product, we should hopefully be taking the trace of the identity-matrix

###which should yield the dimension of the matrix:

print(BTD.TrProd(iBTD))

The individual blocks of the matrix is accessed as

P = BTD.Block(0,1)
plt.matshow(P[0,:,:])

We can multiply the BTD matrix with a block-sparse matrix:

K = block_sparse([[0,0]],[2*a0],(5,5))
print(K.BDot(iBTD).Tr())

Manually inverting the equivalent matrix with numpy gives:

Full = np.zeros(BTD.shape)
S = [0,2,6,10,12,14]
for i in range(5):
    Full[:,S[i]:S[i+1],S[i]:S[i+1]]  = Al [Ia[i]]
    if i<5-1:
        Full [:,S[i]:S[i+1],S[i+1]:S[i+2]]  = Cl [Ic[i]]
        Full [:,S[i+1]:S[i+2],S[i]:S[i+1]]  = Bl [Ib[i]]
iFull = np.linalg.inv(Full)

i,j  = 0,2

print(np.isclose(iBTD.Block(i,j),iFull[:,S[i]:S[i+1],S[j]:S[j+1]]).all())



This algorthm is does of course also work on small matrices, but the advatantages over normal inversion of dense matrices is evident when considering e.g a 2000 x 2000 matrix and we restrict ourselves to e.g. the diagonal blocks:

This script should take about a minute to run and might convince you to use this code if whatever problem you want to solve involves block-tridiagonal matrices:

from Block_matrices import block_td
import numpy  as np
from time import time

inv = np.linalg.inv
r = np.random.random
D1     = 2
D2     = 2
N_test = 20


S      = [np.random.randint(100,120) for i in range(N_test)]
Ia=[i for i in range(N_test   )]
Ib=[i for i in range(N_test-1 )]
Ic=[i for i in range(N_test-1 )]

Al = [r((D1,D2,S[i  ],S[i]))+1j*r((D1,D2,S[i  ],S[i])) for i in range(N_test)]
Bl = [r((D1,D2,S[i+1],S[i]))+1j*r((D1,D2,S[i+1],S[i])) for i in range(N_test-1)]
Cl = [r((D1,D2,S[i],S[i+1]))+1j*r((D1,D2,S[i],S[i+1])) for i in range(N_test-1)]

Full  = np.zeros((D1,D2,sum(S),sum(S)),dtype=np.complex128)
Slices=[]
SUM=0

for i in range(N_test):
    Slices+=[slice(SUM,SUM+S[i])]
    SUM+=S[i]

for i in range(N_test):
    Full[:,:,Slices[i],Slices[i]]  = Al [i]
    if i<N_test-1:
        Full [:,:,Slices[i],Slices[i+1]]  =  Cl [i]
        Full [:,:,Slices[i+1],Slices[i]]  =  Bl [i]
        

BTD  = block_td(Al,Bl,Cl,Ia,Ib,Ic, 
                )
s1 = time()
for i in range(5):
    iFull = inv(Full)

s2 = time()
for i in range(5):
    iBTD = BTD.Invert( BW= 0)# BW = 0 means the diagonal, and can take several different keywords, look at the function in Block_matrices.py
s3 = time()
print('five dense inversions took : ',  s2-s1, ' seconds')
print('five BTD inversions took : ',  s3-s2, ' seconds')

#check which block you want (of the diagonals at least, but more if you change BW to something else):
i,j  = 2,2
si, sj =  BTD.all_slices[i][j]

print('elements close: ',np.isclose(iFull[..., si,sj], iBTD.Block(i,j)).all() )

A handy function for testing is the "Blocksparse2Numpy" function, which takes a block_sparse or block_td class and returns the equivalent dense numpy array:

from Block_matrices import Blocksparse2Numpy
iBTD = BTD.Invert(); # find all blocks of inverse
BTD_dense = Blocksparse2Numpy(BTD, BTD.all_slices)
print('BTD converted to dense is close to Full: ', np.isclose(BTD_dense, Full).all())

iBTD_dense = Blocksparse2Numpy(iBTD, BTD.all_slices) # note the iBTD does not have the "all_slices" attribute, but it shares it will BTD
print('BTD converted to dense is close to Full: ', np.isclose(iBTD_dense, iFull).all())




About

Matrix classes for matrices that are block-tridiagonal and sparse, and simply "block sparse". These talk together, and furthermore containts an algorithm for inversion of the block-tridiagonal version. Much faster than the numpy and scipy equivalents when a particular matrix is block tridiagonal and large enough.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy