0

I have a symmetric matrix a for which the diagonal elements can be different.

>>> import numpy as np
>>> a = np.array([[3, 7, 6], [7, 2, 5], [6, 5, 1]])
>>> a
array([[3, 7, 6],
       [7, 2, 5],
       [6, 5, 1]])

I would like to normalize this matrix to make all the diagonal elements 0 like this:

array([[0, x1, x2],
       [x1, 0, x3],
       [x2, x3, 0]])

How can I do it (in Python if possible)? Any help will be very appreciated, thank you.

  • Thank you for your answers but I should have specified more. My aim is not to fill in the diagonal. I want to normalize all the matrix elements according to the diagonal like: `A = np.multiply(a, X) ` with a=input matrix, A=output matrix with diagonal value = 0 (N.B. the matrix size I use is 20*20) – user14340851 Sep 25 '20 at 15:44
  • So a and A are given and you want to find a matrix X such that A = np.multiply(a, X)? (By the way, do you mean np.multiply or np.dot)? – Elena Fortina Sep 25 '20 at 16:18
  • Sorry, it's `np.dot(a, X)` and `X` is unknown. All we know is the input matrix `a` and the diagonal of output matrix `A` which is zero. – user14340851 Sep 25 '20 at 16:49
  • I updated my initial answer; is that what you needed? Are there other hypotheses / details to consider in your problem? – Elena Fortina Sep 26 '20 at 08:40

2 Answers2

1

You may use the following command: np.fill_diagonal(a, 0) (https://numpy.org/doc/stable/reference/generated/numpy.fill_diagonal.html)


Following your clarifications: if I understand well what you want to do, then you can distinguish between two cases.

  • a invertible ==> use X = np.linalg.solve(a, A)
  • a not invertible ==> in this case there can be either no solution or infinitely many solutions.

For example, if a is not invertible but A is invertible, then there is no solution (otherwise X*A^-1 would provide an inverse for a). In general, a necessary condition for a solution to exist is that rk(A) <= rk(a) (by the rank theorem).

In this other case, there are infinitely many solutions

a = array([[0, 0, 0],
           [0, 2, 0],
           [0, 0, 1]])

A = array([[0., 0., 0.],
           [0., 0., 1.],
           [0., 1., 0.]])

since

    array([[0. , 0. , 0. ],              array([[1., 1., 1.],
X =        [0. , 0. , 0.5],     + lbda *        [0., 0., 0.],
           [0. , 1. , 0. ]])                    [0., 0., 0.]])

solves np.dot(a,X) = A for each real value of lbda.

If you are in this second case (infinitely many solutions), you can use

X = np.linalg.lstsq(a,A)[0]

which provides a solution even in the case when a is not invertible (and returns the same result as np.linalg.solve if a is invertible). If no solution exists, this command returns a matrix such that np.dot(a,X) is "as close as possible" to A. You can realize that this is the case by adding a final check like assert np.max(np.abs(np.dot(a,X) - A)) < 1E-5.

Elena Fortina
  • 152
  • 1
  • 1
  • 11
  • Thank you for your answer. The problem is that we don't know the `A` either: we want its diagonal to be zero but we don't know the values of the other elements. There are too many unknowns in this system. I will try to find a solution to apply the initial matrix `a` without normalization. Anyway, thank you very much for your precious explanation, I learned a lot! – user14340851 Sep 27 '20 at 10:00
0

If you want to make all the diagonal elements 0, just use a simple for-loop.

for i in range(len(matrix)):
    matrix[i][i] = 0
  • 2
    The _amazing_ thing about numpy is you don't have to use loops to change the array. This is the wrong way to change a numpy array – Pranav Hosangadi Sep 25 '20 at 15:38