Ryan
Ryan

Reputation: 433

Generating Symmetric Matrices in Numpy

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

Answers (7)

Apoorv Mote
Apoorv Mote

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

Bruno
Bruno

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

Fábio Reale
Fábio Reale

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

a5kin
a5kin

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

hennyk
hennyk

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

Ben Usman
Ben Usman

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

JoshAdel
JoshAdel

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:

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.

Upvotes: 70

Related Questions