42

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

7 Answers 7

66

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:

http://networkx.lanl.gov

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.

6
  • 1
    Thanks! That is an efficient solution. However, is there some way I can get it to place zeros in places randomly? This matrix is supposed to represent an kind of adjacency matrix for a graph, so having a matrix with randomly distributed zero's is preferable.
    – Ryan
    Commented May 29, 2012 at 21:43
  • 8
    @Ryan: Do you care what kind of distribution the random entries have? If you add b + b.T, you will get a non-uniform distribution concentrated around 0.
    – unutbu
    Commented May 29, 2012 at 21:56
  • I am verifying some properties of matrices. Its more an effort to provide convincing evidence of some mathematical properties, so the distribution here is not so important. Thanks though!
    – Ryan
    Commented May 30, 2012 at 1:17
  • 3
    @unutbu , true, use np.tril(a) + np.tril(a, -1).T then.
    – Ben Usman
    Commented Dec 6, 2014 at 11:57
  • np.random.randint(-5,5,size=(N,N)) seems to have been deprecated need to use randint
    – sushmit
    Commented Jun 30, 2021 at 20:35
46

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).

1
  • 5
    That's a very elegant way of keeping the same distribution!
    – Arash
    Commented Nov 17, 2016 at 13:38
4
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

3

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

1
  • ps: "always invertible" is true for matrices, not for vectors. So, it would be numerically more robust to do mt.rand(size, size). It will return a size x size matrix anyway, but more expensive computationally.
    – Bruno
    Commented Apr 21, 2020 at 18:12
2

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.

0

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]])
0
0

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.

Not the answer you're looking for? Browse other questions tagged or ask your own question.