Reputation: 433
I am trying to generate symmetric matrices in numpy. Specifically, these matrices are to have random places entries, and in each entry the contents can be random. Along the main diagonal we are not concerned with what entries are in there, so I have randomized those as well.
The approach I have taken is to first generate a nxn all zero matrix and simply loop over the indices of the matrices. How can I do this more efficiently using numpy?
import numpy as np
import random
def empty(x, y):
return x*0
b = np.fromfunction(empty, (n, n), dtype = int)
for i in range(0, n):
for j in range(0, n):
if i == j:
b[i][j] = random.randrange(-2000, 2000)
else:
switch = random.random()
random.seed()
if switch > random.random():
a = random.randrange(-2000, 2000)
b[i][j] = a
b[j][i] = a
else:
b[i][j] = 0
b[j][i] = 0
Upvotes: 43
Views: 68953
Reputation: 523
import numpy as np
n = 5
M = np.random.randint(-2000,2000,(n,n))
symm = [email protected]
# test for symmetry
print(symm == symm.T)
This worked for me
Upvotes: 4
Reputation: 61
There is a mathematical property in matrices that allows such structure to be created easily: A.T * A where A is a row vector and A.t is the transpose (a column vector). This always returns a square positive definite symmetric matrix which is always invertible, so you have no worries with null pivots ;)
# any matrix algebra will do it, numpy is simpler
import numpy.matlib as mt
# create a row vector of given size
size = 3
A = mt.rand(1,size)
# create a symmetric matrix size * size
symmA = A.T * A
Upvotes: 3
Reputation: 121
There is an elegant answer here that produces a matrix where all entries follow the same distribution. However, that answer discards (n-1)*n/2
random numbers without using them.
If you want to have all of the values follow the same distribution, generate them all at once and generate only the ones you are going to use, then you can run the following:
>>> import numpy as np
>>> n = 5
>>> r = np.random.rand(n*(n+1)//2)
>>> sym = np.zeros((n,n))
>>> for i in range(n):
... t = i*(i+1)//2
... sym[i,0:i+1] = r[t:t+i+1]
... sym[0:i,i] = r[t:t+i]
...
>>> print(sym)
[[0.03019945 0.30679756 0.85722724 0.78498237 0.56146757]
[0.30679756 0.46276869 0.45104513 0.28677046 0.10779794]
[0.85722724 0.45104513 0.62193894 0.86898652 0.11543257]
[0.78498237 0.28677046 0.86898652 0.13929717 0.45309959]
[0.56146757 0.10779794 0.11543257 0.45309959 0.5671571 ]]
Idea here is to follow the triangle numbers to know how many elements from the random vector have already been used previously. Given this t
value, fill current row up to and including the diagonal and the current column up to (but not including) the diagonal.
Upvotes: 0
Reputation: 1435
I'm using the following function to make a matrix symmetric both vertically and horizontally:
def make_sym(a):
w, h = a.shape
a[w - w // 2 :, :] = np.flipud(a[:w // 2, :])
a[:, h - h // 2:] = np.fliplr(a[:, :h // 2])
Let check how it works:
>>> m = (np.random.rand(10, 10) * 10).astype(np.int)
>>> make_sym(m)
>>> m
array([[2, 7, 5, 7, 7, 7, 7, 5, 7, 2],
[6, 3, 9, 3, 6, 6, 3, 9, 3, 6],
[1, 4, 6, 7, 2, 2, 7, 6, 4, 1],
[9, 2, 7, 0, 8, 8, 0, 7, 2, 9],
[5, 5, 6, 1, 9, 9, 1, 6, 5, 5],
[5, 5, 6, 1, 9, 9, 1, 6, 5, 5],
[9, 2, 7, 0, 8, 8, 0, 7, 2, 9],
[1, 4, 6, 7, 2, 2, 7, 6, 4, 1],
[6, 3, 9, 3, 6, 6, 3, 9, 3, 6],
[2, 7, 5, 7, 7, 7, 7, 5, 7, 2]])
Upvotes: 0
Reputation: 51
If you don't mind having zeros on the diagonal you could use the following snippet:
def random_symmetric_matrix(n):
_R = np.random.uniform(-1,1,n*(n-1)/2)
P = np.zeros((n,n))
P[np.triu_indices(n, 1)] = _R
P[np.tril_indices(n, -1)] = P.T[np.tril_indices(n, -1)]
return P
Note that you only need to generate n*(n-1)/2 random variables due to the symmetry.
Upvotes: 2
Reputation: 8387
I'd better do:
a = np.random.rand(N, N)
m = np.tril(a) + np.tril(a, -1).T
because in this case all elements of a matrix are from same distribution (uniform in this case).
Upvotes: 49
Reputation: 68682
You could just do something like:
import numpy as np
N = 100
b = np.random.random_integers(-2000,2000,size=(N,N))
b_symm = (b + b.T)/2
Where you can choose from whatever distribution you want in the np.random
or equivalent scipy module.
Update: If you are trying to build graph-like structures, definitely check out the networkx package:
which has a number of built-in routines to build graphs:
http://networkx.lanl.gov/reference/generators.html
Also if you want to add some number of randomly placed zeros, you can always generate a random set of indices and replace the values with zero.
Upvotes: 70