Tridiagonal Matrix with Matrix Blocks - python

I read this, but I wasn't able to create a (N^2 x N^2) - matrix A with (N x N) - matrices I on the lower and upper side-diagonal and T on the diagonal. I tried this
def prep_matrix(N):
I_N = np.identity(N)
NV = zeros(N*N - 1)
# now I want NV[0]=NV[1]=...=NV[N-1]:=I_N
but I have no idea how to fill NV with my matrices. What can I do? I found a lot on how to create tridiagonal matrices with scalars, but not with matrix blocks.

You could initialize like this:
n = 3
I, T, A = np.identity(n), np.ones(n), np.zeros([n * n, n * n])
for i in range(n):
a, b, c = i * n, (i + 1) * n, (i + 2) * n
A[a:b, a:b] = T
if i < n - 1: A[b:c, a:b] = A[a:b, b:c] = I
The above example has the following output:
[[ 1. 1. 1. 1. 0. 0. 0. 0. 0.]
[ 1. 1. 1. 0. 1. 0. 0. 0. 0.]
[ 1. 1. 1. 0. 0. 1. 0. 0. 0.]
[ 1. 0. 0. 1. 1. 1. 1. 0. 0.]
[ 0. 1. 0. 1. 1. 1. 0. 1. 0.]
[ 0. 0. 1. 1. 1. 1. 0. 0. 1.]
[ 0. 0. 0. 1. 0. 0. 1. 1. 1.]
[ 0. 0. 0. 0. 1. 0. 1. 1. 1.]
[ 0. 0. 0. 0. 0. 1. 1. 1. 1.]]

For the sake of art, another approach based on bmat():
Z = np.zeros((n, n))
I = np.eye(n)
T = np.ones((n, n))*2
B = np.diag([1]*n) + np.diag([2]*np.ones(n-1), 1) + np.diag(2*np.ones(n-1), -1)
# B = array([[ 1., 2., 0.],
# [ 2., 1., 2.],
# [ 0., 2., 1.]])
# build 2d list `Bm` replacing 0->Z, 1->T, 2->I:
bdict = {0.:Z, 1.:T, 2.:I}
Bm = [[bdict[i] for i in rw] for rw in B]
# Use the power of bmat to construct matrix:
A = np.asarray(np.bmat(Bm))

Related

Python returning two identical matrices

I am trying to write a small program for Markov Decision Process (inventory problem) using Python. I cannot figure out why the program outputs two identical matrices (for profit and decision matrices). The programming itself has some problems too because the last two columns are all zeros, which should not happen. Any help on the program itself would also be super helpful!
import math
import numpy as np
def salvageCost(b):
return 5 * b
def orderingCost(b):
if b == 0:
return 0
else:
return 4 + 2 * b
def holdingCost(b):
return 1.5 * b
def revenue(b):
return 8 * b
M = 10
N = 4
u = np.zeros((M+1,N))
T = np.array([4,3,2,1])
S = np.array(range(M+1))
A = np.array(range(M+1))
u[:,0] = S*5
d = np.zeros((11,4))
probs = np.array([0.05, 0.1, 0.15, 0.2, 0.2, 0.15, 0.1, 0.05, 0, 0, 0])
demands = np.array(range(11))
candidate = [0]*11
d = u
for i in T[1:N]:
for j in S:
for a in range(M-j+1):
candidate[a] = -holdingCost(j) - orderingCost(a) \
+ np.array([probs[k]for k in range(j+a)]).dot(8*np.array([demands[k]for k in range(j+a)])) \
+ np.array([probs[p] for p in range(min(j+a+1,M+1),M+1)]).dot(np.array(range(min(j+a+1,M+1),M+1)))*80\
+ probs.dot(u[:,i-1])
u[j,i] = max(candidate)
d[j,i] = candidate.index(max(candidate))
print(d)
print(u)
This is the output
[[ 0. 0. 0. 0.]
[ 5. 0. 0. 0.]
[10. 0. 0. 0.]
[15. 0. 0. 0.]
[20. 0. 0. 0.]
[25. 0. 0. 0.]
[30. 0. 0. 0.]
[35. 0. 0. 0.]
[40. 0. 0. 0.]
[45. 0. 0. 0.]
[50. 0. 0. 0.]]
[[ 0. 0. 0. 0.]
[ 5. 0. 0. 0.]
[10. 0. 0. 0.]
[15. 0. 0. 0.]
[20. 0. 0. 0.]
[25. 0. 0. 0.]
[30. 0. 0. 0.]
[35. 0. 0. 0.]
[40. 0. 0. 0.]
[45. 0. 0. 0.]
[50. 0. 0. 0.]]
This line:
d = u
points d and u to the same location in memory. As a result, any changes to d are reflected in u, and vice versa.
You are probably looking to create a copy of the contents of u, in which case you can use d = u.copy().
Because numpy.ndarrays are mutable, when you write
d = u
d and u point to the same matrix, so any change to d or to u affects both:
>>> import numpy as np
>>> x = np.array([1, 2])
>>> y = x
>>> y[0] = 10
>>> x
array([10, 2])

Create 4-dimensional NxNxMxM array using 2-dimension NxM array as diagonal

I am looking to perform the following operation: A[i,j,a,b] = δ ij δ ab B[i,a]. Where A is a NxNxMxM array and B is a NxM array and the δ symbols corresponds to the Kronecker delta.
This can be easily done using explicit looping.
A = np.zeros((N,N,M,M))
for i in range(N):
for j in range(N):
for a in range(M):
for b in range(M):
if i == j and a == b:
A[i,j,a,b] = B[i,a]
However, there must be easier and faster ways to perform such operations.
A much shorter way (but still maybe not the most optimal) is to do the following:
Try it online!
import numpy as np
np.random.seed(4)
N, M = 2, 3
B = np.random.randint(0, 100, (N, M))
print(B)
A = np.zeros((N, N, M, M))
arange_m = np.arange(M)
for i in range(N):
A[(i, i, arange_m, arange_m)] = B[i, arange_m]
print(A)
Output:
[[46 55 69]
[ 1 87 72]]
[[[[46. 0. 0.]
[ 0. 55. 0.]
[ 0. 0. 69.]]
[[ 0. 0. 0.]
[ 0. 0. 0.]
[ 0. 0. 0.]]]
[[[ 0. 0. 0.]
[ 0. 0. 0.]
[ 0. 0. 0.]]
[[ 1. 0. 0.]
[ 0. 87. 0.]
[ 0. 0. 72.]]]]
I just found even shorter way doing same thing using mesh-grid:
Try it online!
import numpy as np
np.random.seed(4)
N, M = 2, 3
B = np.random.randint(0, 100, (N, M))
print(B)
A = np.zeros((N, N, M, M))
mg = np.meshgrid(np.r_[:N], np.r_[:M], sparse = True)
A[(mg[0], mg[0], mg[1], mg[1])] = B[(mg[0], mg[1])]
print(A)
Output:
[[46 55 69]
[ 1 87 72]]
[[[[46. 0. 0.]
[ 0. 55. 0.]
[ 0. 0. 69.]]
[[ 0. 0. 0.]
[ 0. 0. 0.]
[ 0. 0. 0.]]]
[[[ 0. 0. 0.]
[ 0. 0. 0.]
[ 0. 0. 0.]]
[[ 1. 0. 0.]
[ 0. 87. 0.]
[ 0. 0. 72.]]]]

In a 10 by 10 matrix, why does this array notation not work?

I would like to know why doesn't my following code output a 1. on the left and right side of the matrix:
import numpy as np
import matplotlib.pyplot as plt
N = 10
def dungeon(N):
dungeon = np.zeros((N, N))
# Top and bottom
dungeon[0] = 1
dungeon[-1] = 1
# Right side
#dungeon[1][0] = 1
#dungeon[2][0] = 1
# etc...
dungeon[0:3][0]
print(dungeon)
Why doesn't dungeon[0:3][0] output 1.'s on the left column? How can I fix this without individually writing dungeon[1][0], dungeon[2][0], etc...?
You are generating a 10x10 Matrix.
dungeon[0] = 1 sets all the elements in the 1st row to 1.
dungeon[-1] = 1 sets all the elements in the last row to 1.
You didn't set the right and left sides.
There are two ways to generate a 10x10 matrix which contains 0s in the center and 1s at the sides:
dungeon = np.ones((10, 10))
dungeon [1:-1, 1:-1] = 0
OR
dungeon = np.zeros((10, 10))
dungeon[0:N,0:1] = 1
dungeon[0:N,-1:N] = 1
dungeon[0:1,0:N] = 1
dungeon[-1:N,0:N] = 1
OUTPUT:
[[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
[1. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[1. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[1. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[1. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[1. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[1. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[1. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[1. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]]
Just take the transpose!
import numpy as np
N = 10
def dungeon(N):
dungeon = np.zeros((N, N))
# Top and bottom
dungeon[0] = 1
dungeon[-1] = 1
# Right and left side
dungeon = dungeon.T
dungeon[0] = 1
dungeon[-1] = 1
print(dungeon)

Update 3 and 4 dimension elements of numpy array

I have a numpy array of shape [12, 8, 5, 5]. I want to modify the values of 3rd and 4th dimension for each element.
For e.g.
import numpy as np
x = np.zeros((12, 80, 5, 5))
print(x[0,0,:,:])
Output:
[[ 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0.]]
Modify values:
y = np.ones((5,5))
x[0,0,:,:] = y
print(x[0,0,:,:])
Output:
[[ 1. 1. 1. 1. 1.]
[ 1. 1. 1. 1. 1.]
[ 1. 1. 1. 1. 1.]
[ 1. 1. 1. 1. 1.]
[ 1. 1. 1. 1. 1.]]
I can modify for all x[i,j,:,:] using two for loops. But, I was wondering if there is any pythonic way to do it without running two loops. Just curious to know :)
UPDATE
Actual use case:
dict_weights = copy.deepcopy(combined_weights)
for i in range(0, len(combined_weights[each_layer][:, 0, 0, 0])):
for j in range(0, len(combined_weights[each_layer][0, :, 0, 0])):
# Extract 5x5
trans_weight = combined_weights[each_layer][i,j]
trans_weight = np.fliplr(np.flipud(trans_weight ))
# Update
dict_weights[each_layer][i, j] = trans_weight
NOTE: The dimensions i, j of combined_weights can vary. There are around 200 elements in this list with varied i and j dimensions, but 3rd and 4th dimensions are always same (i.e. 5x5).
I just want to know if I can updated the elements combined_weights[:,:,5, 5] with transposed values without running 2 for loops.
Thanks.
Simply do -
dict_weights[each_layer] = combined_weights[each_layer][...,::-1,::-1]

Python: Appending dimensions to a bidimensional array

Suppose you have an array (m, m) and want to make it (n, n). For example, transforming a 2x2 matrix to a 6x6. So:
[[ 1. 2.]
[ 3. 4.]]
To:
[[ 1. 2. 0. 0. 0. 0.]
[ 3. 4. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0.]]
This is what I'm doing:
def array_append(old_array, new_shape):
old_shape = old_array.shape
dif = np.array(new_shape) - np.array(old_array.shape)
rows = []
for i in xrange(dif[0]):
rows.append(np.zeros((old_array.shape[0])).tolist())
new_array = np.append(old_array, rows, axis=0)
columns = []
for i in xrange(len(new_array)):
columns.append(np.zeros(dif[1]).tolist())
return np.append(new_array, columns, axis=1)
Example use:
test1 = np.ones((2,2))
test2 = np.zeros((6,6))
print array_append(test1, test2.shape)
Output:
[[ 1. 1. 0. 0. 0. 0.]
[ 1. 1. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0.]]
Based on this answer. But that's a lot of code for an (imho) simple operation. Is there a more concise/pythonic way to do it?
Why not use array = numpy.zeros((6,6)), see the numpy docs...
EDIT, woops, question has been edited... I guess you are trying to put ones in a section of an array filled with zeros? Then:
array = numpy.zeros((6,6))
array[0:2,0:2] = 1
If the small matrix does not all have the value of 1:
array[ystart:yend,xstart:xend] = smallermatrix
That would be then:
# test1= np.ones((2, 2))
test1= np.random.randn((2, 2))
test2= np.zeros((6, 6))
test2[0: 2, 0: 2]= test1

Categories