#+TITLE: Testing Svd #+title: Testing SVD #+author: Vijay Gopal Chilkuri #+email: vijay.gopal.c@gmail.com * Generate a block random distribution of positions Here's a small function to generate random points inside a 3D box of given length. #+name: generate3Dpoints #+begin_src python def get_sphere_distribution(n, dmin, Ls, maxiter=1e4, allow_wall=True): """Get random points in a box with given dimensions and minimum separation. Parameters: - n: number of points - dmin: minimum distance - Ls: dimensions of box, shape (3,) array - maxiter: maximum number of iterations. - allow_wall: whether to allow points on wall; (if False: points need to keep distance dmin/2 from the walls.) Return: - ps: array (n, 3) of point positions, with 0 <= ps[:, i] < Ls[i] - n_iter: number of iterations - dratio: average nearest-neighbor distance, divided by dmin. Note: with a fill density (sphere volume divided by box volume) above about 0.53, it takes very long. (Random close-packed spheres have a fill density of 0.64). Author: Han-Kwang Nienhuys (2020) Copying: BSD, GPL, LGPL, CC-BY, CC-BY-SA See Stackoverflow: https://stackoverflow.com/a/62895898/6228891 """ Ls = np.array(Ls).reshape(3) if not allow_wall: Ls -= dmin # filling factor; 0.64 is for random close-packed spheres # This is an estimate because close packing is complicated near the walls. # It doesn't work well for small L/dmin ratios. sphere_vol = np.pi/6*dmin**3 box_vol = np.prod(Ls + 0.5*dmin) fill_dens = n*sphere_vol/box_vol if fill_dens > 0.64: msg = f'Too many to fit in the volume, density {fill_dens:.3g}>0.64' raise ValueError(msg) # initial try ps = np.random.uniform(size=(n, 3)) * Ls # distance-squared matrix (diagonal is self-distance, don't count) dsq = ((ps - ps.reshape(n, 1, 3))**2).sum(axis=2) dsq[np.arange(n), np.arange(n)] = np.infty for iter_no in range(int(maxiter)): # find points that have too close neighbors close_counts = np.sum(dsq < dmin**2, axis=1) # shape (n,) n_close = np.count_nonzero(close_counts) if n_close == 0: break # Move the one with the largest number of too-close neighbors imv = np.argmax(close_counts) # new positions newp = np.random.uniform(size=3)*Ls ps[imv]= newp # update distance matrix new_dsq_row = ((ps - newp.reshape(1, 3))**2).sum(axis=-1) dsq[imv, :] = dsq[:, imv] = new_dsq_row dsq[imv, imv] = np.inf else: raise RuntimeError(f'Failed after {iter_no+1} iterations.') if not allow_wall: ps += dmin/2 dratio = (np.sqrt(dsq.min(axis=1))/dmin).mean() return ps, iter_no+1, dratio #+end_src A function that generates a block random set of points #+name: generateBlocks #+begin_src python :noweb yes <> def generateBlockRandomPointsAtShftApart(n,L1,dmin,shift): """ Parameters: - n: number of points - L1: dimensions of box, shape (3,) array - dmin: minimum intra block distance - shift: inter block distance Return: - r: array (n, 3) of point positions, """ Ls = np.array([L1,L1,L1]) # lengths of the box # Quadrant +,+ dist11,a,b = get_sphere_distribution(n,dmin,Ls) dist11 += np.array([shift,shift,0.0]) # Quadrant -,+ dist21,a,b = get_sphere_distribution(n,dmin,Ls) dist21 += np.array([-shift,shift,0.0]) # Quadrant +,- dist12,a,b = get_sphere_distribution(n,dmin,Ls) dist12 += np.array([shift,-shift,0.0]) # Quadrant -,- dist22,a,b = get_sphere_distribution(n,dmin,Ls) dist22 += np.array([-shift,-shift,0.0]) xs = np.concatenate((dist11[:,0],dist12[:,0],dist21[:,0],dist22[:,0])) ys = np.concatenate((dist11[:,1],dist12[:,1],dist21[:,1],dist22[:,1])) zs = np.concatenate((dist11[:,2],dist12[:,2],dist21[:,2],dist22[:,2])) return((np.array((xs,ys,zs))).T) #+end_src #+RESULTS: generateBlocks #+begin_example None #+end_example #+begin_src python :noweb yes :results file :exports results import numpy as np # matplotlib related import matplotlib.pyplot as plt <> L1 = 1.0 n = 100 # number of points dmin = 0.1 # min dist between points Ls = np.array([L1,L1,L1]) # lengths of the box shift = -10.0 kappa = 2.0 rlist = generateBlockRandomPointsAtShftApart(n,L1,dmin,shift) print(rlist.shape) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') xs = rlist.T[0] ys = rlist.T[1] zs = rlist.T[2] ax.scatter(xs, ys, zs, marker='o') fig.savefig('/tmp/test8.png') #plt.show() return '/tmp/test8.png' #+end_src #+RESULTS: [[file:/tmp/test8.png]] #+begin_src python :noweb yes :results file :exports results # matplotlib related import matplotlib.pyplot as plt # linear algebra import numpy as np <> shift = -1.0 # Quadrant +,+ L1 = 1.0 n = 50 # number of points dmin = 0.1 # min dist between points Ls = np.array([L1,L1,L1]) # lengths of the box dist11,a,b = get_sphere_distribution(n,dmin,Ls) dist11 += np.array([shift,shift,0.0]) # Quadrant -,+ L1 = 1.0 n = 50 # number of points dmin = 0.1 # min dist between points Ls = np.array([L1,L1,L1]) # lengths of the box dist21,a,b = get_sphere_distribution(n,dmin,Ls) dist21 += np.array([-shift,shift,0.0]) # Quadrant +,- L1 = 1.0 n = 50 # number of points dmin = 0.1 # min dist between points Ls = np.array([L1,L1,L1]) # lengths of the box dist12,a,b = get_sphere_distribution(n,dmin,Ls) dist12 += np.array([shift,-shift,0.0]) # Quadrant -,- L1 = 1.0 n = 50 # number of points dmin = 0.1 # min dist between points Ls = np.array([L1,L1,L1]) # lengths of the box dist22,a,b = get_sphere_distribution(n,dmin,Ls) dist22 += np.array([-shift,-shift,0.0]) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') xs = np.concatenate((dist11[:,0],dist12[:,0],dist21[:,0],dist22[:,0])) ys = np.concatenate((dist11[:,1],dist12[:,1],dist21[:,1],dist22[:,1])) zs = np.concatenate((dist11[:,2],dist12[:,2],dist21[:,2],dist22[:,2])) ax.scatter(xs, ys, zs, marker='o') fig.savefig('/tmp/test3.png') #plt.show() return '/tmp/test3.png' #+end_src #+RESULTS: [[file:/tmp/test3.png]] * Is the SVD decomposition of distances really useful ? The problem is the following: ** Given a metric F(r1,r2) finding the best representatin of F(r1,r2) *** Slater metric The metric can be a scaled distance such as \[ F(r_1,r_2) = \exp(-\kappa(|r_1 - r_2|)) \] Where,\(\kappa\) is the rate of the breadth of the slater. #+begin_src python :results file :exports results # matplotlib related import matplotlib import matplotlib.pyplot as plt # linear algebra import numpy # Data for plotting kappa = 1.0/2.0 xstart = 0.0 xend = 2.0 xstep = 0.1 s = numpy.array(list(map(lambda x : numpy.exp(-x * numpy.arange(xstart,xend,xstep)), [100, 25, 10,5,2,1]))).T #s = numpy.exp(-kappa * numpy.arange(0,1,0.1)) t = numpy.arange(xstart,xend,xstep) fig, ax = plt.subplots() ax.plot(t, s) ax.set(xlabel=r'$r_{12}$', ylabel=r'$F(r_1,r_2)$', title='Comparison of Kappa') #ax.set_yscale('log') ax.grid() fig.savefig('/tmp/test1.png') #plt.show() return '/tmp/test1.png' #+end_src #+RESULTS: [[file:/tmp/test1.png]] *** Gaussian metric The metric can be a scaled distance such as \[ F(r_1,r_2) = \exp(-\kappa(|r_1 - r_2|^2)) \] Where,\(\kappa\) is the rate of the breadth of the gaussian. #+begin_src python :results file :exports results # matplotlib related import matplotlib import matplotlib.pyplot as plt # linear algebra import numpy # Data for plotting kappa = 1.0/2.0 xstart = 0.0 xend = 2.0 xstep = 0.1 s = numpy.array(list(map(lambda x : numpy.exp(-x * numpy.power(numpy.arange(xstart,xend,xstep),2)), [100, 25, 10,5,2,1]))).T #s = numpy.exp(-kappa * numpy.arange(0,1,0.1)) t = numpy.arange(xstart,xend,xstep) fig, ax = plt.subplots() ax.plot(t, s) ax.set(xlabel=r'$r_{12}$', ylabel=r'$F(r_1,r_2)$', title='Comparison of Kappa') #ax.set_yscale('log') ax.grid() fig.savefig('/tmp/test2.png') #plt.show() return '/tmp/test2.png' #+end_src #+RESULTS: [[file:/tmp/test2.png]] * Generating a box of electrons centered around nucleii ** Generate randomly distributed nucleii The nucleii will serve as anchors for the screening of distances for calculating the screened \(\tilde{F}(r_1,r_2)\) metric. * Calculating SVD of the distance vector ** Slater metric Calculate the matrix of the \(F(r_1,r_2)\) metric i.e. the slater metric. #+begin_src python :noweb yes :results file :exports results import numpy as np from functools import reduce import matplotlib.pyplot as plt <> L1 = 1.0 n = 10 # number of points dmin = 0.01 # min dist between points Ls = np.array([L1,L1,L1]) # lengths of the box shift = -1.0 kappa = 0.6 rlist = generateBlockRandomPointsAtShftApart(n,L1,dmin,shift) print(rlist.shape) rij = np.zeros(shape=(rlist.shape[0],rlist.shape[0])) def funcF(x,y): return(np.exp(-kappa * np.linalg.norm(x-y))) rij = np.array([[funcF(xval, yval) for yval in rlist] for xval in rlist]) u,d,vt = np.linalg.svd(rij) #print(d) #plt.imshow(rij) #plt.colorbar() #plt.show() plt.plot(range(d.shape[0]),d) plt.yscale('log') plt.savefig('/tmp/plot3.png') return '/tmp/plot3.png' #+end_src #+RESULTS: [[file:/tmp/plot3.png]] #+begin_src python :results output import numpy a = numpy.array([[1,2,3],[4,5,6],[7,8,9]]) b = numpy.array([[11,12,13],[14,15,16],[17,18,19]]) print(list(zip(a,b))[0][1]) print(numpy.square(a[:,0])) def stepExp(a): def myexp(x): if numpy.abs(x) > 1e+0: return numpy.zeros_like(x) else: return numpy.exp(x) res = numpy.array([[myexp(x) for x in y] for y in a]) return(res) print(numpy.exp(a)) print(stepExp(a)) #+end_src #+RESULTS: #+begin_example [11 12 13] [ 1 16 49] [[2.71828183e+00 7.38905610e+00 2.00855369e+01] [5.45981500e+01 1.48413159e+02 4.03428793e+02] [1.09663316e+03 2.98095799e+03 8.10308393e+03]] [[2.71828183 0. 0. ] [0. 0. 0. ] [0. 0. 0. ]] #+end_example ** Gaussian metric Calculate the matrix of the \(FG(r_1,r_2)\) metric i.e. the gaussian metric. #+begin_src python :noweb yes :results file :exports results import numpy as np from functools import reduce import matplotlib.pyplot as plt <> L1 = 1.0 n = 100 # number of points dmin = 0.1 # min dist between points Ls = np.array([L1,L1,L1]) # lengths of the box shift = -10.0 kappa = 2.0 rlist = generateBlockRandomPointsAtShftApart(n,L1,dmin,shift) print(rlist.shape) rij = np.zeros(shape=(rlist.shape[0],rlist.shape[0])) def funcF(x,y): return(np.exp(-kappa * np.linalg.norm(x-y))) def funcFG(x,y): return(np.exp(-kappa * np.square(np.linalg.norm(x-y)))) def funcFGD(x,y): rij = np.exp(-kappa * 0.1 * np.square(np.linalg.norm(x-y))) return(rij) rijSlater = np.array([[funcF(xval, yval) for yval in rlist] for xval in rlist]) rijGaussian = np.array([[funcFG(xval, yval) for yval in rlist] for xval in rlist]) rijDeltafn = np.array([[funcFGD(xval, yval) for yval in rlist] for xval in rlist]) u,dS,vt = np.linalg.svd(rijSlater) dS = dS/np.linalg.norm(dS) u,dG,vt = np.linalg.svd(rijGaussian) dG = dG/np.linalg.norm(dG) u,dGD,vt = np.linalg.svd(rijDeltafn) dGD = dGD/np.linalg.norm(dGD) #print(d) #plt.imshow(rij) #plt.colorbar() #plt.show() plt.plot(range(dG.shape[0]),np.array([dS,dG,dGD]).T) plt.yscale('log') plt.savefig('/tmp/plot4.png') return '/tmp/plot4.png' #+end_src #+RESULTS: [[file:/tmp/plot4.png]] ** Palying around Calculate the matrix of the \(FG(r_1,r_2)\) metric i.e. the gaussian metric. #+begin_src python :noweb yes :results file :exports results import numpy as np from functools import reduce import matplotlib.pyplot as plt <> L1 = 1.0 n = 100 # number of points dmin = 0.1 # min dist between points Ls = np.array([L1,L1,L1]) # lengths of the box shift = -1.0 kappa = 2.0 rlist = generateBlockRandomPointsAtShftApart(n,L1,dmin,shift) print(rlist.shape) rij = np.zeros(shape=(rlist.shape[0],rlist.shape[0])) def funcF(x,y): rij = np.exp(-kappa * np.linalg.norm(x-y)) return(rij) def funcFG(x,y): rij = np.exp(-kappa * np.square(np.linalg.norm(x-y))) return(rij) def myexp(x): if np.abs(x) > 1e-0: return np.exp(-x) else: return np.exp(x) def funcFGD(x,y): rij = myexp(-kappa * np.square(np.linalg.norm(x-y))) return(rij) rijSlater = np.array([[funcF(xval, yval) for yval in rlist] for xval in rlist]) #rijSlater = rijSlater/np.max(rijSlater) rijGaussian = np.array([[funcFG(xval, yval) for yval in rlist] for xval in rlist]) #rijGaussian = rijGaussian/np.max(rijGaussian) rijDeltafn = np.array([[funcFGD(xval, yval) for yval in rlist] for xval in rlist]) #rijDeltafn = rijDeltafn/np.max(rijDeltafn) u,dS,vt = np.linalg.svd(rijSlater) dS = dS/np.linalg.norm(dS) u,dG,vt = np.linalg.svd(rijGaussian) dG = dG/np.linalg.norm(dG) u,dGD,vt = np.linalg.svd(rijDeltafn) dGD = dGD/np.linalg.norm(dGD) #print(d) #plt.imshow(rij) #plt.colorbar() #plt.show() plt.plot(range(dG.shape[0]),np.array([dS,dG,dGD]).T) plt.yscale('log') plt.savefig('/tmp/plot5.png') return '/tmp/plot5.png' #+end_src #+RESULTS: [[file:/tmp/plot5.png]] #+begin_src python :results file :exports results import numpy import matplotlib.pyplot as plt def myexp2(x): if numpy.abs(x) > 1e-0: return numpy.exp(-x) else: return numpy.exp(x) def myexp(x): return(numpy.array([myexp2(y) for y in x])) kappa = 1.0/2.0 xstart = 0.0 xend = 2.0 xstep = 0.1 s = numpy.array(list(map(lambda x : myexp(-x * numpy.power(numpy.arange(xstart,xend,xstep),2)), [10,5,1,0.5,0.1]))).T #s = numpy.exp(-kappa * numpy.arange(0,1,0.1)) t = numpy.arange(xstart,xend,xstep) fig, ax = plt.subplots() ax.plot(t, s) ax.set(xlabel=r'$r_{12}$', ylabel=r'$F(r_1,r_2)$', title='Comparison of Kappa') ax.set_yscale('log') ax.grid() fig.savefig('/tmp/test7.png') #plt.show() return '/tmp/test7.png' #+end_src #+RESULTS: [[file:/tmp/test7.png]] ** Testing SVD for custom matrices #+begin_src python :results output import numpy import matplotlib.pyplot as plt a = numpy.array([[0,100,200],[100,0,200],[100,200,0]]) b = numpy.exp(-a) print("Matrix A") print(a) print("Matrix Exp(A)") print(numpy.around(b,10)) u,d,vt = numpy.linalg.svd(a) d = d/numpy.linalg.norm(d) print("Singular values of A") print(numpy.around(d,3)) print("Singular vectors of A") print(numpy.around(u,3)) u,d,vt = numpy.linalg.svd(b) d = d/numpy.linalg.norm(d) print("Singular values of Exp(A)") print(numpy.around(d,3)) print("Singular vectors of Exp(A)") print(numpy.around(u,3)) #+end_src #+RESULTS: #+begin_example Matrix A [[ 0 100 200] [100 0 200] [100 200 0]] Matrix Exp(A) [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]] Singular values of A [0.813 0.53 0.24 ] Singular vectors of A [[-0.67 0.142 0.728] [-0.626 0.42 -0.657] [-0.399 -0.896 -0.193]] Singular values of Exp(A) [0.577 0.577 0.577] Singular vectors of Exp(A) [[-1. 0. -0. ] [-0. -0.894 -0.447] [-0. -0.447 0.894]] #+end_example