# Diagonalize matrix numerically over $\mathbb{C}$

Suppose I have a matrix m:

$$m = \left(\begin{array}{rr} 2 & -3 \\ 1 & 0 \end{array}\right).$$ It is diagonalizable and has complex eigenvalues. I now want to diagonalize it, but get an error:

In [20]: m = matrix([[2, -3], [1, 0]]); m.diagonalization()
...
ValueError: matrix entries must be from a field


When I specify the field m = matrix(CDF, [[2, -3], [1, 0]]), I get ValueError: base field must be exact, but Complex Double Field is not. Specifying ComplexLazyField() instead of CDF raises NotImplementedError.

So, apparently, Sage is trying to diagonalize the matrix symbolically, i.e. exactly. But what if I don't care about exactness and just want a straightforward numerical answer?

This is how I would do it with sympy:

In [22]: import sympy as sp
...: m = sp.Matrix([[2, -3], [1, 0]])
...: m.diagonalize()
Out[22]:
(Matrix([
[1 - sqrt(2)*I, 1 + sqrt(2)*I],
[            1,             1]]),
Matrix([
[1 - sqrt(2)*I,             0],
[            0, 1 + sqrt(2)*I]]))


Notice that the output is actually exact. I know I can run this same Python code in Sage, but I assume there's a more native way to do it.

To sum up, how do I get Sage to diagonalize a matrix over $\mathbb{C}$? How do I change the code if I only need the numerical answer?

edit retag close merge delete

Sort by » oldest newest most voted

The errors message are quite clear: you should provide a field that is exact. This is because of numerical stability issues in the algorithms.

Let us try with the first exact field that contains ZZ that we have in mind:

sage: m = matrix(QQ,[[2, -3], [1, 0]])
sage: m.diagonalization()
ValueError: not diagonalizable over Rational Field


The answer tells that the eigenvalues are not rational, so we need to use a larger field: the field of algebraic numbers:

sage: m = m.change_ring(QQbar)
sage: m.diagonalization()
(
[1 - 1.414213562373095?*I                        0]  [                                          1                                           1]
[                       0 1 + 1.414213562373095?*I], [0.3333333333333334? + 0.4714045207910317?*I 0.3333333333333334? - 0.4714045207910317?*I]
)


Wile the results looks numerical, but it is only for screen printing, they are actually genuine algebraic numbers, see for the first entry:

sage: m.diagonalization()[0][0][0]
1 - 1.414213562373095?*I
sage: parent(m.diagonalization()[0][0][0])
Algebraic Field
sage: (m.diagonalization()[0][0][0]).minpoly()
x^2 - 2*x + 3


Now, if you want to get only numerical result, you can do:

sage: m.diagonalization()[0].change_ring(CDF)
[1.0 - 1.414213562373095*I                       0.0]
[                      0.0 1.0 + 1.414213562373095*I]
sage: m.diagonalization()[1].change_ring(CDF)
[                                       1.0                                        1.0]
[0.33333333333333337 + 0.4714045207910317*I 0.33333333333333337 - 0.4714045207910317*I]


By the way, instead of changinf the base ring of the matrix, you can change it for the computation of the diagonalization:

sage: m = matrix([[2, -3], [1, 0]])
sage: m.diagonalization(QQbar)
(
[1 - 1.414213562373095?*I                        0]  [                                          1                                           1]
[                       0 1 + 1.414213562373095?*I], [0.3333333333333334? + 0.4714045207910317?*I 0.3333333333333334? - 0.4714045207910317?*I]
)

more

Thank you for a good answer, I accepted it. My mistake was I didn't try other fields. I should have googled for algebraic complex numbers, i.e. QQbar. Also didn't know you can change the field after constructing the matrix.

( 2021-10-07 11:42:28 +0200 )edit

Is there a way to display the numerically-looking numbers the way sympy does it? I.e. sqrt(2) instead of 1.414213562373?

( 2021-10-07 11:45:23 +0200 )edit

Note that when you use change_ring method, the original matrix is not modified, but a new one is constructed, this is why i wrote m = m.change_ring(QQbar) and not m.change_ring(QQbar).

( 2021-10-07 11:45:57 +0200 )edit