Search code examples
pythonmatrixsympydiagonal

How to replace the the diagonal elements of a matrix by a vector in SymPy?


I have a vector X which I created like this:

from sympy import *

x1 = Symbol('x1')
x2 = Symbol('x2')
x3 = Symbol('x3')

X = Matrix([x1, x2, x3])

Then I also have a matrix myMat which just contains ones:

myMat = ones(3, 3)

Matrix([
[1, 1, 1],
[1, 1, 1],
[1, 1, 1]])

Now I would like to replace the diagonal of the matrix by my vector X; my desired outcome looks like this:

Matrix([
[x1, 1, 1],
[1, x2, 1],
[1, 1, x3]])

I can of course do it in a for-loop like this:

for ind, el in enumerate(X):
    myMat[ind, ind] = el

but I am wondering whether there is a smarter way of doing that by directly accessing the diagonal of this matrix. While I can calculate the trace of the matrix, I could not find a way to replace just the diagonal elements using something like myMat.diag = X. Is there a way of doing that?

EDIT

@Emilien got me on the right track and therefore I accepted this answer. Building up on this answer, I also posted my own solution which makes use of sympy and numpy and solves the problem in one single line: my answer


Solution

  • You can build it with diagonal and identity matrices, I'm not sure it's much better on a performance point of vue thought, but maybe it's easier to understand when reading the code if that's what you're looking for.

    x1, x2, x3 = symbols('x1 x2 x3')
    mat = diag(x1,x2,x3)-eye(3)+ones(3)
    

    or

    l = symbols('x1 x2 x3')
    mat = diag(*l)-eye(3)+ones(3)
    

    As you wish.

    Another tricky solution, maybe less readable:

    l = symbols('x1 x2 x3')
    Matrix(3, 3, lambda i,j: l[i] if i==j else 1)
    

    Finally, if you do not wish to modify the original

    l = symbols('x1 x2 x3')
    M = Matrix(([1,2,3],[4,5,6],[7,8,9]))
    M = Matrix(3, 3, lambda i,j: l[i] if i==j else M[i,j])