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?
Related
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
I have a 10*10 2D numpy array/list where some values are 0 and others are 1. No two 1 can be set diagonally, which means if array[3][4] is 1,then array[2][4], array[4,4], array[3][3] and array[3][5] can't be 1. So I write this code:
if arr[i,j]:
if arr[i+1,j] or arr[i-1,j] or arr[i,j-1] or arr[i,j+1]:
return False
But the problem is I can't loop through this code all the way from i = 0 to i = 9 because for i = 0 and i = 9 there will be list out of index error.
So I had to rewrite the code:
if arr[i,j]:
if (i>0 and i<9) and (j>0 and j<9):
if arr[i+1,j] or arr[i-1,j] or arr[i,j-1] or arr[i,j+1]:
return False
And then I have to write a if-else for i==0 and j==0, then for i==0 and j==9 and then for i == 0 and (j > 0 or j < 9) and some more.
Can anybody suggest a shortcut way to solve the problem in one if-else condition without getting the list out of index error.
You can apply a convolution across your matrix with 0.5 on the diagonals and wherever it returns a 1, there are two ones in a diagonal at that position.
example:
array = np.array([[1, 1, 1],
[1, 0, 0],
[0, 0, 0]])
applying a convolution to the main diagonal direction (\):
from scipy.signal import convolve
convolve(array, np.array([[0.5, 0],[0,0.5]]), mode="valid")
output:
array([[0.5, 0.5],
[0.5, 0. ]])
There are no '1's so this passes
Now applying in the other direction (the anti-diagonal, /)
convolve(array, np.array([[0, 0.5],[0.5,0]]), mode="valid")
output:
array([[1. , 0.5],
[0. , 0. ]])
There is a 1 on the top left square of this convolution, so there are two 1's in the anti-diagonal of the original array in the top left corner.
Related to this question: How to have negative zero always formatted as positive zero in a python string?
I have the following function that implements Matlab's orth.m using Numpy. I have a docstring test that relies on np.array2string using suppress_small=True, which will make small values round to zero. However, sometimes they round to positive zero and sometimes they round to negative zero, depending on whether the answer comes out as 1e-16 or -1e-17 or similar. Which case happens is based on the SVD decomposition, and can vary from platform to platform or across Python versions depending on which underlying linear algebra solver is used (BLAS, Lapack, etc.)
What's the best way to design the docstring test to account for this?
In the final doctest, sometimes the Q[0, 1] term is -0. and sometimes it's 0.
import doctest
import numpy as np
def orth(A):
r"""
Orthogonalization basis for the range of A.
That is, Q.T # Q = I, the columns of Q span the same space as the columns of A, and the number
of columns of Q is the rank of A.
Parameters
----------
A : 2D ndarray
Input matrix
Returns
-------
Q : 2D ndarray
Orthogonalization matrix of A
Notes
-----
#. Based on the Matlab orth.m function.
Examples
--------
>>> import numpy as np
Full rank matrix
>>> A = np.array([[1, 0, 1], [-1, -2, 0], [0, 1, -1]])
>>> r = np.linalg.matrix_rank(A)
>>> print(r)
3
>>> Q = orth(A)
>>> with np.printoptions(precision=8):
... print(Q)
[[-0.12000026 -0.80971228 0.57442663]
[ 0.90175265 0.15312282 0.40422217]
[-0.41526149 0.5664975 0.71178541]]
Rank deficient matrix
>>> A = np.array([[1, 0, 1], [0, 1, 0], [1, 0, 1]])
>>> r = np.linalg.matrix_rank(A)
>>> print(r)
2
>>> Q = orth(A)
>>> print(np.array2string(Q, precision=8, suppress_small=True)) # Sometimes this fails
[[-0.70710678 -0. ]
[ 0. 1. ]
[-0.70710678 0. ]]
"""
# compute the SVD
(Q, S, _) = np.linalg.svd(A, full_matrices=False)
# calculate a tolerance based on the first eigenvalue (instead of just using a small number)
tol = np.max(A.shape) * S[0] * np.finfo(float).eps
# sum the number of eigenvalues that are greater than the calculated tolerance
r = np.sum(S > tol, axis=0)
# return the columns corresponding to the non-zero eigenvalues
Q = Q[:, np.arange(r)]
return Q
if __name__ == '__main__':
doctest.testmod(verbose=False)
You can print the rounded array plus 0.0 to eliminate the -0:
A = np.array([[1, 0, 1], [0, 1, 0], [1, 0, 1]])
Q = orth(A)
Q[0,1] = -1e-16 # simulate a small floating point deviation
print(np.array2string(Q.round(8)+0.0, precision=8, suppress_small=True))
#[[-0.70710678 0. ]
# [ 0. 1. ]
# [-0.70710678 0. ]]
So your doc string should be:
>>> Q = orth(A)
>>> print(np.array2string(Q.round(8)+0.0, precision=8, suppress_small=True)) # guarantee non-negative zeros
[[-0.70710678 0. ]
[ 0. 1. ]
[-0.70710678 0. ]]
Here's another alternative I came up with, although I think I like rounding the array to the given precision better. In this method, you shift the whole array by some amount that is bigger than the round-off error, but smaller than the precision comparison. That way the small numbers will still always be slightly positive.
>>> Q = orth(A)
>>> print(np.array2string(Q + np.full(Q.shape, 1e-14), precision=8, suppress_small=True))
[[-0.70710678 0. ]
[ 0. 1. ]
[-0.70710678 0. ]]
Sorry if this post is a dupli,I couldn't find an answer... I have the following code:
import numpy as np
V = np.array([[6, 10, 0],
[2, 5, 0],
[0, 0, 0]])
subarr = np.array([[arr[0][0], arr[0][1]], [arr[1][0], arr[1][1]]])
det = np.linalg.det(subarr)
cross = np.cross(arr[0], arr[1])
print(f"Det: {det}")
print(f"Cross: {cross}")
I would expect that the det would return 10.0 and the cross returns in this case [0, 0, 10], the last number being equal to the det. For some reason, python returns
Det: 10.000000000000002
Cross: [ 0 0 10]
Can someone please explain why?
What you're seeing is floating point inaccuracies.
And in case you're wondering how you end up with floats when finding the determinant of a matrix made up of integers (where the usual calculation method is just 6*5 - 2*10 = 10), np.linalg.det uses LU decomposition to find the determinant. This isn't very efficient for 2x2 matrices, but is much more efficient when you have bigger matrices.
For your 2x2, you get:
scipy.linalg.lu(A, 1)
Out:
(array([[ 1. , 0. ],
[ 0.33333333, 1. ]]),
array([[ 6. , 10. ],
[ 0. , 1.66666667]]))
The determinant is just the product of the diagonals from this, which ends up being 6. * 1.66666667, which resolves to 10.00000002 due to floating point errors.
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]]