Generating Symmetric Matrices in Numpy

Question:

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
Asked By: Ryan

||

Answers:

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.

Answered By: JoshAdel

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

Answered By: Ben Usman

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.

Answered By: asardon

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]])
Answered By: a5kin

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.

Answered By: Fábio Reale

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

Answered By: Bruno
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

Answered By: Apoorv Mote