Tridiagonal Matrix using Python - python

I looked on the past questions on tridiagonals but none seem to be experiencing the problem i'm having. I'm trying to form a tridiagonal stiffness matrix for the non uniform Poisson equation using scipy.sparse.spdiags but do not seem to be receiving a matrix as a result.
def Poisson_Stiffness(x0):
N = len(x0) - 1 #THE AMOUNT OF ELEMENTS (NOT THE AMOUNT OF POINTS) x0, x1, ... , x_N
h = np.zeros(N)
a = np.zeros(N+1)
b = np.zeros(N)
for i in range(N):
h[i] = x0[i+1] - x0[i] #Length of each nonuniform element
a[0] = 1/h[0]
for i in range(1,N):
a[i] = 1/h[i] + 1/h[i-1] #Main Diagonal of stiffness matrix
a[N] = 1/h[N-1]
for i in range(N):
b[i] = -1/h[i] #Upper and lower diagonal of stiffness matrix.
Tridiagonal_Data = np.array([[a],[b],[b]])
Positions = [0, 1, -1]
Stiffness_Matrix = scipy.sparse.spdiags(Tridiagonal_Data,Positions,N+1,N+1)
print Stiffness_Matrix
As a result from this, with x0 = [0,0.3,0.4,0.7,1]; I am getting the stiffness matrix as :
Jamess-MBP:Poisson jamesmalone$ python Poisson1d.py
(0, 0) [ 3.33333333 13.33333333 13.33333333 6.66666667 3.33333333]
(1, 0) [-3.33333333 -10. -3.33333333 -3.33333333]
My question is why does it come out like this and not in matrix form?
I have tried changing the data types to see if that was the problem but i would receive errors such as (for .toarray()):
Jamess-MBP:Poisson jamesmalone$ python Poisson1d.py
Traceback (most recent call last):
File "Poisson1d.py", line 33, in <module>
Poisson_Stiffness([0,0.3,0.4,0.7,1])
File "Poisson1d.py", line 29, in Poisson_Stiffness
Stiffness_Matrix = scipy.sparse.spdiags(Tridiagonal_Data,Positions,N+1,N+1).toarray()
File "/Users/jamesmalone/anaconda/lib/python2.7/site-packages/scipy/sparse/base.py", line 637, in toarray
return self.tocoo().toarray(order=order, out=out)
File "/Users/jamesmalone/anaconda/lib/python2.7/site-packages/scipy/sparse/coo.py", line 275, in toarray
B.ravel('A'), fortran)
RuntimeError: internal error: failed to resolve data types
Thanks in advance.

Try using scipy.sparse.diags. I also cleaned up your code because you are not taking advantage of numpy's strengths (broadcasting) with those for loops. Also cleaned up some formatting according to PEP8:
from scipy.sparse import diags
x0 = np.array(x0)
N = len(x0) - 1
h = x0[1:] - x0[:-1]
a = np.zeros(N+1)
a[0] = 1/h[0]
a[1:-1] = 1/h[1:] + 1/h[:-1]
a[-1] = 1/h[-1]
b = -1/h
data = [a.tolist(), b.tolist(), b.tolist()]
positions = [0, 1, -1]
stiffness_matrix = diags(data, positions, (N+1, N+1))
print stiffness_matrix.toarray()
With x0 = [0, 0.3, 0.4, 0.7, 1], this yields
[[ 3.33333333 -3.33333333 0. 0. 0. ]
[ -3.33333333 13.33333333 -10. 0. 0. ]
[ 0. -10. 13.33333333 -3.33333333 0. ]
[ 0. 0. -3.33333333 6.66666667 -3.33333333]
[ 0. 0. 0. -3.33333333 3.33333333]]

Related

Detection of groups of connected spheres

I need to detect which spheres are connected to each other. If we have:
radii = np.array([2, 1, 1, 2, 2, 0.5])
poss = np.array([[7, 7, 7], [7.5, 8.5, 6], [0, 0, 0], [-1, -2, -1], [1, 1, 1], [2, 1, 3]])
I want a Boolean array (shape = (number of groups, number of spheres)) or array/lists of arrays/lists of indices that shows which of the spheres are connected. So, the expected results for this example must be something like:
Boolean_array = np.array([[1, 1, 0, 0, 0, 0], [0, 0, 1, 1, 1, 1]], dtype=bool)
object_array = np.array([[0, 1], [2, 3, 4, 5]])
I tried to find a solution with networkx (I'm not very familiar with it) and IDK if this library can help where we have spheres with different radii. I guess, returned ends_ind in my previous code can be helpful in this regard and I tried to use that as:
G = nx.Graph([*ends_ind])
L = [nx.node_connected_component(G, 0)]
for i in range(len(radii)):
iter = 0
for j in L:
if i in j:
iter += 1
if iter == 0:
L.append(nx.node_connected_component(G, i))
Which will not work. The error:
Traceback (most recent call last):
File "C:/Users/Ali/Desktop/check_2.py", line 31, in <module>
L.append(nx.node_connected_component(G, i))
File "<class 'networkx.utils.decorators.argmap'> compilation 8", line 4, in argmap_node_connected_component_5
File "C:\Users\Ali\anaconda3\envs\PFC_FiPy\lib\site-packages\networkx\algorithms\components\connected.py", line 185, in node_connected_component
return _plain_bfs(G, n)
File "C:\Users\Ali\anaconda3\envs\PFC_FiPy\lib\site-packages\networkx\algorithms\components\connected.py", line 199, in _plain_bfs
nextlevel.update(G_adj[v])
File "C:\Users\Ali\anaconda3\envs\PFC_FiPy\lib\site-packages\networkx\classes\coreviews.py", line 82, in __getitem__
return AtlasView(self._atlas[name])
KeyError: 11
Since using my previous code with other libraries will be an inefficient code (if it can solve the issue), I am seeking for any libraries, e.g. networkx, or methods that can do it in a more efficient way, if possible.
What is the best way to get my expected results, particularly for large number of spheres (~100000).
You're trying to utilize networkx too early, here. First, you should calculate the geometrical distances for each pair of spheres. A useful trick for this is:
xyz_distances = poss.reshape(6, 1, 3) - poss.reshape(1, 6, 3)
distances = np.linalg.norm(xyz_distances, axis=2)
This gets you a symmetric 6x6 array of Euclidean distances between the sphere centers. Now, we need to compare the maximum possible distances. This is just the sum of radii for each pair of spheres, once again a 6x6 array, which we can calculate as
maximum_distances = radii.reshape(6, 1) + radii.reshape(1, 6)
And now we can compare the two:
>>> connections = distances < maximum_distances
>>> connections
array([[ True, True, False, False, False, False],
[ True, True, False, False, False, False],
[False, False, True, True, True, False],
[False, False, True, True, False, False],
[False, False, True, False, True, True],
[False, False, False, False, True, True]])
Which translates to two groups, just like you wanted - and you can get your second expected array via
>>> G = nx.Graph(connections)
>>> list(nx.connected_components(G))
[{0, 1}, {2, 3, 4, 5}]
Note that this whole thing is going to scale as N^2 in the number of spheres, and you might need to optimize that somehow (say, via scipy.spatial.ckdtree).
In one of my tests on 18000 spheres, NumPy's linalg leaked the memory, but SciPy's cdist was more memory efficient and worked [ref 1]. It seems the calculations can be limited to JUST triangle upper the diameter of the arrays, which can be more efficient in terms of memory usage and time consumption. Thanks to Dominik answer, we can do this process using Numba accelerator as parallelized no python mode:
import numpy as np
import numba as nb
from scipy.spatial.distance import cdist
import networkx as nx
def distances_linalg(radii, poss):
xyz_distances = poss.reshape(radii.shape[0], 1, 3) - poss.reshape(1, radii.shape[0], 3)
return radii.reshape(radii.shape[0], 1) + radii.reshape(1, radii.shape[0]), np.linalg.norm(xyz_distances, axis=2)
def distances_cdist(radii, poss):
return radii.reshape(radii.shape[0], 1) + radii.reshape(1, radii.shape[0]), cdist(poss, poss)
#nb.njit("(Tuple([float64[:, ::1], float64[:, ::1]]))(float64[::1], float64[:, ::1])", parallel=True)
def distances_numba(radii, poss):
radii_arr = np.zeros((radii.shape[0], radii.shape[0]), dtype=np.float64)
poss_arr = np.zeros((poss.shape[0], poss.shape[0]), dtype=np.float64)
for i in nb.prange(radii.shape[0] - 1):
for j in range(i+1, radii.shape[0]):
radii_arr[i, j] = radii[i] + radii[j]
poss_arr[i, j] = ((poss[i, 0] - poss[j, 0]) ** 2 + (poss[i, 1] - poss[j, 1]) ** 2 + (poss[i, 2] - poss[j, 2]) ** 2) ** 0.5
return radii_arr, poss_arr
def connected_spheres(radii, poss, method=distances_numba):
maximum_distances, distances = method(radii, poss)
connections = distances < maximum_distances
G = nx.Graph(connections)
return list(nx.connected_components(G))
# numba radii cdist or linalg radii
# [[0. 3. 3. 4. 4. 2.5] [[4. 3. 3. 4. 4. 2.5]
# [0. 0. 2. 3. 3. 1.5] [3. 2. 2. 3. 3. 1.5]
# [0. 0. 0. 3. 3. 1.5] [3. 2. 2. 3. 3. 1.5]
# [0. 0. 0. 0. 4. 2.5] [4. 3. 3. 4. 4. 2.5]
# [0. 0. 0. 0. 0. 2.5] [4. 3. 3. 4. 4. 2.5]
# [0. 0. 0. 0. 0. 0. ]] [2.5 1.5 1.5 2.5 2.5 1. ]]
# numba poss
# [[ 0. 1.87082869 12.12435565 14.45683229 10.39230485 8.77496439]
# [ 0. 0. 12.82575534 15.21512405 11.11305539 9.77241014]
# [ 0. 0. 0. 2.44948974 1.73205081 3.74165739]
# [ 0. 0. 0. 0. 4.12310563 5.83095189]
# [ 0. 0. 0. 0. 0. 2.23606798]
# [ 0. 0. 0. 0. 0. 0. ]]
# cdist or linalg poss
# [[ 0. 1.87082869 12.12435565 14.45683229 10.39230485 8.77496439]
# [ 1.87082869 0. 12.82575534 15.21512405 11.11305539 9.77241014]
# [12.12435565 12.82575534 0. 2.44948974 1.73205081 3.74165739]
# [14.45683229 15.21512405 2.44948974 0. 4.12310563 5.83095189]
# [10.39230485 11.11305539 1.73205081 4.12310563 0. 2.23606798]
# [ 8.77496439 9.77241014 3.74165739 5.83095189 2.23606798 0. ]]
Which in my test on 18000 spheres was at least 2 times faster than cdist. I think, numba will be very helpful to avoid memory leaks on large arrays comparing to cdist.
Solution 2:
We can write distances_numba based on an improved cdist code by numba. In this solution I tried to modify that code to adjust it just on the upper triangle of the arrays:
#nb.njit("float64[:, ::1](float64[:, ::1])", parallel=True)
def dot_triu(poss):
assert poss.shape[1] == 3
poss_T = poss.T
dot = np.zeros((poss.shape[0], poss.shape[0]), dtype=poss.dtype)
for i in nb.prange(poss.shape[0] - 1):
for j in range(i + 1, poss.shape[0]):
dot[i, j] = poss[i, 0] * poss_T[0, j] + poss[i, 1] * poss_T[1, j] + poss[i, 2] * poss_T[2, j]
return dot
#nb.njit("float64[::1](float64[:, ::1])", parallel=True)
def poss_(poss):
TMP_A = np.zeros(poss.shape[0], dtype=np.float64)
for i in nb.prange(poss.shape[0]):
for j in range(poss.shape[1]):
TMP_A[i] += poss[i, j] ** 2
return TMP_A
#nb.njit("(Tuple([float64[:, ::1], float64[:, ::1]]))(float64[::1], float64[:, ::1])", parallel=True)
def distances_numba(radii, poss):
poss_arr = dot_triu(poss)
TMP_A = poss_(poss)
radii_arr = np.zeros((radii.shape[0], radii.shape[0]), dtype=np.float64)
for i in nb.prange(poss.shape[0] - 1):
for j in range(i + 1, poss.shape[0]):
radii_arr[i, j] = radii[i] + radii[j]
poss_arr[i, j] = (-2. * poss_arr[i, j] + TMP_A[i] + TMP_A[j]) ** 0.5
return radii_arr, poss_arr

Rank issue with spdiags in Python

I am currently trying to create a sparse matrix that will look like this.
[[ 50. -25. 0. 0.]
[-25. 50. -25. 0.]
[ 0. -25. 50. -25.]
[ 0. 0. -25. 50.]]
But when I run it through I keep getting the value error
'data array must have rank 2' in my data array.
I am positive it is a problem with my B variable. I have tried several things but nothing is working. Any advice?
def sparse(a,b,N):
h = (b-a)/(N+1)
e = np.ones([N,1])/h**2
B = np.array([e, -2*e, e])
diags = np.array([-1,0,1])
A = spdiags(B,diags,N,N).toarray()
return A
print(sparse(0,1,4))
Just change to this:
import numpy as np
from scipy.sparse import spdiags
def sparse(a, b, N):
h = (b - a) / (N + 1)
e = np.ones(N) / h ** 2
diags = np.array([-1, 0, 1])
A = spdiags([-1 * e, 2 * e, -1 * e], diags, N, N).toarray()
return A
print(sparse(0, 1, 4))
Output
[[-50. 25. 0. 0.]
[ 25. -50. 25. 0.]
[ 0. 25. -50. 25.]
[ 0. 0. 25. -50.]]
The main change is this:
e = np.ones([N,1])/h**2
by
e = np.ones(N) / h ** 2
Note that toarray transforms the sparse matrix into a dense one, from the documentation:
Return a dense ndarray representation of this matrix.

Calculate pixelwise distance of 3D tensor in tensorflow?

I am trying to create a 3d distance map (size: W * H * D) in tensorflow to be used in a loss function for training. I have a ground truth (binary volume of size W * H * D) that I will use to create the distance map, i.e. the value of each pixel of my distance map will be the minimum distance of that pixel to the positive valued (i.e pixel=1) shape in the ground truth.
Having issues with the 3d shape problem as L2.NORM reduce the axis to a 2D shape and making this problem fully differentiable. Any advice or pointers would be much appreciated.
If I understand correctly, you want to compute the distance from each position in the volume to the closest position of a given class. For simplicity, I will assume that the interesting class is labelled with 1, but hopefully you can adapt it to your case if it is different. The code is for TensorFlow 2.0, but should work the same for 1.x.
The simplest way to do this is to compute the distance between all the coordinates in the volume against every coordinate with a 1, and then pick the smallest distance from there. You can do that like this:
import tensorflow as tf
# Make input data
w, h, d = 10, 20, 30
w, h, d = 2, 3, 4
t = tf.random.stateless_uniform([w, h, d], (0, 0), 0, 2, tf.int32)
print(t.numpy())
# [[[0 1 0 0]
# [0 0 0 0]
# [1 1 0 1]]
#
# [[1 0 0 0]
# [0 0 0 0]
# [1 1 0 0]]]
# Make coordinates
coords = tf.meshgrid(tf.range(w), tf.range(h), tf.range(d), indexing='ij')
coords = tf.stack(coords, axis=-1)
# Find coordinates that are positive
m = t > 0
coords_pos = tf.boolean_mask(coords, m)
# Find every pairwise distance
vec_d = tf.reshape(coords, [-1, 1, 3]) - coords_pos
# You may choose a difference precision type here
dists = tf.linalg.norm(tf.dtypes.cast(vec_d, tf.float32), axis=-1)
# Find minimum distances
min_dists = tf.reduce_min(dists, axis=-1)
# Reshape
out = tf.reshape(min_dists, [w, h, d])
print(out.numpy().round(3))
# [[[1. 0. 1. 2. ]
# [1. 1. 1.414 1. ]
# [0. 0. 1. 0. ]]
#
# [[0. 1. 1.414 2.236]
# [1. 1. 1.414 1.414]
# [0. 0. 1. 1. ]]]
This may work well enough for you, although it may not be the most efficient solution. The smartest thing would be to search for the closest positive position in the neighboring area of each position, but that is complicated to do effectively, both in general and more so in a vectorized way in TensorFlow. There are however a couple of ways we can improve on the code above. On the one hand, we know that positions with a 1 will always have zero distance, so computing for those is unnecessary. On the other hand, if the 1 class in the 3D volume represents some kind of dense shape, then we could save some time if we only computed the distances against the surface of that shape. All other positive positions will have necessarily a greater distance to positions outside the shape. So we can do the same thing we were doing, but computing only distances from non-positive positions to positive surface positions. You can do that like this:
import tensorflow as tf
# Make input data
w, h, d = 10, 20, 30
w, h, d = 2, 3, 4
t = tf.dtypes.cast(tf.random.stateless_uniform([w, h, d], (0, 0)) > .15, tf.int32)
print(t.numpy())
# [[[1 1 1 1]
# [1 1 1 1]
# [1 1 0 0]]
#
# [[1 1 1 1]
# [1 1 1 1]
# [1 1 1 1]]]
# Find coordinates that are positive and on the surface
# (surrounded but at least one 0)
t_pad_z = tf.pad(t, [(1, 1), (1, 1), (1, 1)]) <= 0
m_pos = t > 0
m_surround_z = tf.zeros_like(m_pos)
# Go through the 6 surrounding positions
for i in range(3):
for s in [slice(None, -2), slice(2, None)]:
slices = tuple(slice(1, -1) if i != j else s for j in range(3))
m_surround_z |= t_pad_z.__getitem__(slices)
# Surface points are positive points surrounded by some zero
m_surf = m_pos & m_surround_z
coords_surf = tf.where(m_surf)
# Find coordinates that are zero
coords_z = tf.where(~m_pos)
# Find every pairwise distance
vec_d = tf.reshape(coords_z, [-1, 1, 3]) - coords_surf
dists = tf.linalg.norm(tf.dtypes.cast(vec_d, tf.float32), axis=-1)
# Find minimum distances
min_dists = tf.reduce_min(dists, axis=-1)
# Put minimum distances in output array
out = tf.scatter_nd(coords_z, min_dists, [w, h, d])
print(out.numpy().round(3))
# [[[0. 0. 0. 0.]
# [0. 0. 0. 0.]
# [0. 0. 1. 1.]]
#
# [[0. 0. 0. 0.]
# [0. 0. 0. 0.]
# [0. 0. 0. 0.]]]
EDIT: Here is one way in which you can divide the distance computations in chunks with a TensorFlow loop:
# Following from before
coords_surf = ...
coords_z = ...
CHUNK_SIZE = 1_000 # Choose chunk size
dtype = tf.float32
# If using TF 2.x you can know in advance the size of the tensor array
# (although the element shape will not be constant due to the last chunk)
num_z = tf.shape(coords_z)[0]
arr = tf.TensorArray(dtype, size=(num_z - 1) // CHUNK_SIZE + 1, element_shape=[None], infer_shape=False)
_, arr = tf.while_loop(lambda i, arr: i < num_z,
lambda i, arr: (i + CHUNK_SIZE, arr.write(i // CHUNK_SIZE,
tf.reduce_min(tf.linalg.norm(tf.dtypes.cast(
tf.reshape(coords_z[i:i + CHUNK_SIZE], [-1, 1, 3]) - coords_surf,
dtype), axis=-1), axis=-1))),
[tf.constant(0, tf.int32), arr])
min_dists = arr.concat()
out = tf.scatter_nd(coords_z, min_dists, [w, h, d])

Numpy's eigh and eig yield inconsistent eigenvalues

Currently I'm trying to solve the generalized eigenvalue problem in NumPy for two symmetric matrices and I've been running into massive trouble as I'm expecting all eigenvalues to be positive, but eigh returns several very large numbers that are not all positive, while eig returns the correct, expected values (but is, of course, very, very slow).
In this case, note that K is symmetric as expected from its construction (here is the code in question):
# Calculate K matrix (<i|pHp|j> in the LGL-nodes basis)
for i in range(Ne):
idx_s, idx_e = i*(Np-1), i*(Np-1)+Np
K[idx_s:idx_e, idx_s:idx_e] += dmat.T.dot(diag(w*peq[idx_s:idx_e])).dot(dmat)
# Re-make matrix for efficient vector products
K = sparse.csr_matrix(K)
# Make matrix for <i|p|j> in the LGL basis as efficient diagonal sparse matrix
S = sparse.diags(peq*w_d, 0)
# Solve the generalized eigenvalue problem: Kc = lSc for hermitian matrices K and S
lQ, Q = linalg.eigh(K.todense(), S.todense())
_lQ, _Q = linalg.eig(K.todense(), S.todense())
lQ.sort()
_lQ.sort()
if not allclose(lQ, _lQ):
print('Literally why')
print(lQ)
print(_lQ)
return
For testing, dmat is defined as
array([[ -896. , 1212.00631086, -484.43454844, 275.06612251,
-179.85209531, 124.26620323, -83.05199285, 32. ],
[ -205.43460499, 0. , 290.78944413, -135.17191772,
82.83085126, -55.64467829, 36.70818656, -14.07728095],
[ 50.7185076 , -179.61445086, 0. , 184.03311398,
-87.85829324, 54.08144362, -34.37053351, 13.01021241],
[ -23.81762789, 69.05246008, -152.20398294, 0. ,
152.89115899, -72.66291308, 42.31407046, -15.57316561],
[ 15.57316561, -42.31407046, 72.66291308, -152.89115899,
0. , 152.20398294, -69.05246008, 23.81762789],
[ -13.01021241, 34.37053351, -54.08144362, 87.85829324,
-184.03311398, 0. , 179.61445086, -50.7185076 ],
[ 14.07728095, -36.70818656, 55.64467829, -82.83085126,
135.17191772, -290.78944413, 0. , 205.43460499],
[ -32. , 83.05199285, -124.26620323, 179.85209531,
-275.06612251, 484.43454844, -1212.00631086, 896. ]])
And all of w[i], w_d[i], peq[i] are essentially arbitrary positive-valued arrays. w_d and w are of the same order (~ 1e-1) and peq[i] ranges on the order of (~ 1e-10 to 1e1)
Some of the output I'm getting is
Literally why
[ -6.25540943e+07 -4.82660391e+07 -2.62629052e+07 ..., 1.07960873e+10
1.07967334e+10 4.26007915e+10]
[ -5.25462340e-12+0.j 4.62614812e-01+0.j 1.23357898e+00+0.j ...,
2.17613917e+06+0.j 1.07967334e+10+0.j 4.26007915e+10+0.j]
EDIT:
Here's a self-contained version of the code for easier debugging
import numpy as np
from math import *
from scipy import sparse, linalg
# Variable declarations and such (pre-computed)
Ne, Np = 256, 8
N = Ne*Np - Ne + 1
domain_size = 4/Ne
x = np.array([-0.015625 , -0.01362094, -0.00924532, -0.0032703 , 0.0032703 ,
0.00924532, 0.01362094, 0.015625 ])
w = np.array([ 0.00055804, 0.00329225, 0.00533004, 0.00644467, 0.00644467,
0.00533004, 0.00329225, 0.00055804])
dmat = np.array([[ -896. , 1212.00631086, -484.43454844, 275.06612251,
-179.85209531, 124.26620323, -83.05199285, 32. ],
[ -205.43460499, 0. , 290.78944413, -135.17191772,
82.83085126, -55.64467829, 36.70818656, -14.07728095],
[ 50.7185076 , -179.61445086, 0. , 184.03311398,
-87.85829324, 54.08144362, -34.37053351, 13.01021241],
[ -23.81762789, 69.05246008, -152.20398294, 0. ,
152.89115899, -72.66291308, 42.31407046, -15.57316561],
[ 15.57316561, -42.31407046, 72.66291308, -152.89115899,
0. , 152.20398294, -69.05246008, 23.81762789],
[ -13.01021241, 34.37053351, -54.08144362, 87.85829324,
-184.03311398, 0. , 179.61445086, -50.7185076 ],
[ 14.07728095, -36.70818656, 55.64467829, -82.83085126,
135.17191772, -290.78944413, 0. , 205.43460499],
[ -32. , 83.05199285, -124.26620323, 179.85209531,
-275.06612251, 484.43454844, -1212.00631086, 896. ]])
# More declarations
x_d = np.zeros(N)
w_d = np.zeros(N)
dmat_d = np.zeros((N, N))
for i in range(Ne):
x_d[i*(Np-1):i*(Np-1)+Np] = x+i*domain_size
w_d[i*(Np-1):i*(Np-1)+Np] += w
dmat_d[i*(Np-1):i*(Np-1)+Np, i*(Np-1):i*(Np-1)+Np] += dmat
peq = (np.cos((x_d-2)*pi/4))**2
# Normalization
peq = peq/np.sum(w_d*peq)
p0 = np.maximum(peq, 1e-10)
p0 /= np.sum(p0*w_d)
# Make efficient matrix that can be built
K = sparse.lil_matrix((N, N))
# Calculate K matrix (<i|pHp|j> in the LGL-nodes basis)
for i in range(Ne):
idx_s, idx_e = i*(Np-1), i*(Np-1)+Np
K[idx_s:idx_e, idx_s:idx_e] += dmat.T.dot(np.diag(w*p0[idx_s:idx_e])).dot(dmat)
# Re-make matrix for efficient vector products
K = sparse.csr_matrix(K)
# Make matrix for <i|p|j> in the LGL basis as efficient diagonal sparse matrix
S = sparse.diags(p0*w_d, 0)
# Solve the generalized eigenvalue problem: Kc = lSc for hermitian matrices K and S
lQ, Q = linalg.eigh(K.todense(), S.todense())
_lQ, _Q = linalg.eig(K.todense(), S.todense())
lQ.sort()
_lQ.sort()
if not np.allclose(lQ, _lQ):
print('Literally why')
print(lQ)
print(_lQ)
EDIT2: This is really odd. Running all of the NumPy/SciPy tests on my machine, I receive no errors. But even running the simple test (with large enough matrices) as
import numpy as np
from spicy import linalg
M = np.random.random((1000,1000))
M += M.T
np.allclose(sorted(linalg.eigh(M)[0]), sorted(linalg.eig(M)[0]))
fails on my machine. Though running the same test with a 50x50 matrix does work---even after rebuilding the SciPy/NumPy stack and passing all unit tests.
EDIT3: Actually, this seems to fail everywhere, after testing it on a cluster computer. I'm not sure why.
The above fails due to the in-place behaviour of += and .T as a view rather than an operation.

How to solve recurrence relations in Python

I am trying to write code to give numerical answers to a recurrence relation. The relation itself is simple and is defined as follows. The variable x is an integer
p(i) = p(i+2)/2 + p(i-1)/2 if i > 0 and i < x
p(0) = p(2)/2
p(i) = 1 if i >= x
This is also in this code.
from __future__ import division
def p(i):
if (i == 0):
return p(2)/2
if (i >= x):
return 1
return p(i-1)/2+p(i+2)/2
x = 4
#We would like to print p(0) for example.
This of course doesn't actually let you compute p(0). How can you do this in python?
Is it possible to set up a system of simultaneous equations which numpy.linalg.solve can then solve?
You're right this can be solved using linear algebra. What I've done below is a simple hard-coded translation. Your equations for p(0) to p(3) are coded up by rearranging them so that the right hand side is =0. For p(4) and p(5) which appear in the recurrence relations as base cases, there is an =1 on the right hand side.
-p(0) + p(2)/2 = 0
p(i-1)/2 - p(i) + p(i+2)/2 = 0 for i > 0 and i < x
p(i) = 1 if i >= x
Here is the program hardcoded for n=4
import numpy
a=numpy.array([[-1, 0, 0.5, 0, 0, 0], # 0
[0.5, -1, 0,0.5, 0, 0], # 1
[0, 0.5, -1, 0, 0.5, 0], # 2
[0, 0, 0.5, -1, 0, 0.5], # 3
[0, 0, 0, 0, 1, 0], # 4
[0, 0, 0, 0, 0, 1], # 5
])
b=numpy.array([0,0,0,0,1,1])
# solve ax=b
x = numpy.linalg.solve(a, b)
print x
Edit, here is the code which constructs the matrix programmatically, only tested for n=4!
n = 4
# construct a
diag = [-1]*n + [1]*2
lowdiag = [0.5]*(n-1) + [0]*2
updiag = [0.5]*n
a=numpy.diag(diag) + numpy.diag(lowdiag, -1) + numpy.diag(updiag, 2)
# solve ax=b
b=numpy.array([0]*n + [1]*2)
x = numpy.linalg.solve(a, b)
print a
print x[:n]
This outputs
[[-1. 0. 0.5 0. 0. 0. ]
[ 0.5 -1. 0. 0.5 0. 0. ]
[ 0. 0.5 -1. 0. 0.5 0. ]
[ 0. 0. 0.5 -1. 0. 0.5]
[ 0. 0. 0. 0. 1. 0. ]
[ 0. 0. 0. 0. 0. 1. ]]
[ 0.41666667 0.66666667 0.83333333 0.91666667]
which matches the solution in your comment under your question.
This is not an answer to the posted question, but this page is the top Google hit for "solve recurrence relation in Python" so I will write an answer.
If you have a linear recurrence and you want to find the recursive formula, you can use Sympy's find_linear_recurrence function. For example, suppose you have the following sequence: 0, 1, 3, 10, 33, 109, 360, 1189, 3927, 12970. Then the following code produces the recurrence relation:
import sympy
from sympy.abc import n
L = [0, 1, 3, 10, 33, 109, 360, 1189, 3927, 12970]
print(sympy.sequence(L, (n, 1, len(L))).find_linear_recurrence(len(L)))
The output is:
[3, 1]
So you know A(n) = 3*A(n-1) + A(n-2).
The issue here is that you end up in an infinite recursion regardless of where you start, because the recursion isn't explicit, but rather ends up yielding systems of linear equations to solve. If this were a problem you had to solve using Python, I would use Python to calculate the coefficients of this system of equations and use Cramer's rule to solve it.
Edit: Specifically, your unknowns are p(0), ..., p(x-1). One coefficient row vector right off the bat is (1, 0, -1/2, 0, ..., 0) (from p(0)-p(2)/2=0), and all the others are of the form (..., -1/2, 1, 0, -1/2, ...). There are x-1 of these (one for each of p(1), ..., p(x-1)) so the system either has a unique solution or none at all. Intuitively, it seems like there should always be a unique solution.
The two last equations would be unique since they would feature p(x) and p(x+1), so those terms would be ommitted; the column vector for the RHS of Cramer's rule would then be (0, 0, ..., 0, 1/2, 1/2), I believe.
Numpy has matrix support.
I'm confused because your code seems like it should do just that.
def p(i):
x = 4 # your constant should be defined in-function
if (i == 0):
return p(2)/2
elif (i >= x):
return 1
return p(i-1)/2+p(i+2)/2
The big problem here is your recursion. For p(1) it does:
p(0)/2 + p(3)/2
p(2)/2 + p(2)/2 + p(4)/2
p(1)/2 + p(1)/2 + 1/2
# each side of the operator is now the same as the original problem!
# that's a sure sign of infinite recursion.
What do you EXPECT to be the output?

Categories