6302 | | |
| 6302 | |
| 6303 | def is_diagonalizable(self, base_field=None): |
| 6304 | r""" |
| 6305 | Determines if the matrix is similar to a diagonal matrix. |
| 6306 | |
| 6307 | INPUT: |
| 6308 | |
| 6309 | - ``base_field`` - a new field to use for entries |
| 6310 | of the matrix. |
| 6311 | |
| 6312 | OUTPUT: |
| 6313 | |
| 6314 | If ``self`` is the matrix `A`, then it is diagonalizable |
| 6315 | if there is an invertible matrix `S` and a diagonal matrix |
| 6316 | `D` such that |
| 6317 | |
| 6318 | .. math:: |
| 6319 | |
| 6320 | S^{-1}AS = D |
| 6321 | |
| 6322 | This routine returns ``True`` if ``self`` is diagonalizable. |
| 6323 | The diagonal entries of the matrix `D` are the eigenvalues |
| 6324 | of `A`. It may be necessary to "increase" the base field to |
| 6325 | contain all of the eigenvalues. Over the rationals, the field |
| 6326 | of algebraic integers, :mod:`sage.rings.qqbar` is a good choice. |
| 6327 | |
| 6328 | To obtain the matrices `S` and `D` use the :meth:`jordan_form` |
| 6329 | method with the ``transformation=True`` keyword. |
| 6330 | |
| 6331 | ALGORITHM: |
| 6332 | |
| 6333 | For each eigenvalue, this routine checks that the algebraic |
| 6334 | multiplicity (number of occurences as a root of the characteristic |
| 6335 | polynomial) is equal to the geometric multiplicity (dimension |
| 6336 | of the eigenspace), which is sufficient to ensure a basis of |
| 6337 | eigenvectors for the columns of `S`. |
| 6338 | |
| 6339 | EXAMPLES: |
| 6340 | |
| 6341 | A matrix that is diagonalizable over the rationals, as evidenced |
| 6342 | by its Jordan form. :: |
| 6343 | |
| 6344 | sage: A = matrix(QQ, [[-7, 16, 12, 0, 6], |
| 6345 | ... [-9, 15, 0, 12, -27], |
| 6346 | ... [ 9, -8, 11, -12, 51], |
| 6347 | ... [ 3, -4, 0, -1, 9], |
| 6348 | ... [-1, 0, -4, 4, -12]]) |
| 6349 | sage: A.jordan_form(subdivide=False) |
| 6350 | [ 2 0 0 0 0] |
| 6351 | [ 0 3 0 0 0] |
| 6352 | [ 0 0 3 0 0] |
| 6353 | [ 0 0 0 -1 0] |
| 6354 | [ 0 0 0 0 -1] |
| 6355 | sage: A.is_diagonalizable() |
| 6356 | True |
| 6357 | |
| 6358 | A matrix that is not diagonalizable over the rationals, as evidenced |
| 6359 | by its Jordan form. :: |
| 6360 | |
| 6361 | sage: A = matrix(QQ, [[-3, -14, 2, -1, 15], |
| 6362 | ... [4, 6, -2, 3, -8], |
| 6363 | ... [-2, -14, 0, 0, 10], |
| 6364 | ... [3, 13, -2, 0, -11], |
| 6365 | ... [-1, 6, 1, -3, 1]]) |
| 6366 | sage: A.jordan_form(subdivide=False) |
| 6367 | [-1 1 0 0 0] |
| 6368 | [ 0 -1 0 0 0] |
| 6369 | [ 0 0 2 1 0] |
| 6370 | [ 0 0 0 2 1] |
| 6371 | [ 0 0 0 0 2] |
| 6372 | sage: A.is_diagonalizable() |
| 6373 | False |
| 6374 | |
| 6375 | If any eigenvalue of a matrix is outside the base ring, then |
| 6376 | this routine raises an error. However, the ring can be |
| 6377 | "expanded" to contain the eigenvalues. :: |
| 6378 | |
| 6379 | sage: A = matrix(QQ, [[1, 0, 1, 1, -1], |
| 6380 | ... [0, 1, 0, 4, 8], |
| 6381 | ... [2, 1, 3, 5, 1], |
| 6382 | ... [2, -1, 1, 0, -2], |
| 6383 | ... [0, -1, -1, -5, -8]]) |
| 6384 | |
| 6385 | sage: [e in QQ for e in A.eigenvalues()] |
| 6386 | [False, False, False, False, False] |
| 6387 | sage: A.is_diagonalizable() |
| 6388 | Traceback (most recent call last): |
| 6389 | ... |
| 6390 | RuntimeError: an eigenvalue of the matrix is not contained in Rational Field |
| 6391 | |
| 6392 | sage: [e in QQbar for e in A.eigenvalues()] |
| 6393 | [True, True, True, True, True] |
| 6394 | sage: A.is_diagonalizable(base_field=QQbar) |
| 6395 | True |
| 6396 | |
| 6397 | Other exact fields may be employed, though it will not always |
| 6398 | be possible to expand their base fields to contain all |
| 6399 | the eigenvalues. :: |
| 6400 | |
| 6401 | sage: F.<b> = FiniteField(5^2) |
| 6402 | sage: A = matrix(F, [[ 4, 3*b + 2, 3*b + 1, 3*b + 4], |
| 6403 | ... [2*b + 1, 4*b, 0, 2], |
| 6404 | ... [ 4*b, b + 2, 2*b + 3, 3], |
| 6405 | ... [ 2*b, 3*b, 4*b + 4, 3*b + 3]]) |
| 6406 | sage: A.jordan_form() |
| 6407 | [ 4 1| 0 0] |
| 6408 | [ 0 4| 0 0] |
| 6409 | [---------------+---------------] |
| 6410 | [ 0 0|2*b + 1 1] |
| 6411 | [ 0 0| 0 2*b + 1] |
| 6412 | sage: A.is_diagonalizable() |
| 6413 | False |
| 6414 | |
| 6415 | sage: F.<c> = QuadraticField(-7) |
| 6416 | sage: A = matrix(F, [[ c + 3, 2*c - 2, -2*c + 2, c - 1], |
| 6417 | ... [2*c + 10, 13*c + 15, -13*c - 17, 11*c + 31], |
| 6418 | ... [2*c + 10, 14*c + 10, -14*c - 12, 12*c + 30], |
| 6419 | ... [ 0, 2*c - 2, -2*c + 2, 2*c + 2]]) |
| 6420 | sage: A.jordan_form(subdivide=False) |
| 6421 | [ 4 0 0 0] |
| 6422 | [ 0 -2 0 0] |
| 6423 | [ 0 0 c + 3 0] |
| 6424 | [ 0 0 0 c + 3] |
| 6425 | sage: A.is_diagonalizable() |
| 6426 | True |
| 6427 | |
| 6428 | A trivial matrix is diagonalizable, trivially. :: |
| 6429 | |
| 6430 | sage: A = matrix(QQ, 0, 0) |
| 6431 | sage: A.is_diagonalizable() |
| 6432 | True |
| 6433 | |
| 6434 | A matrix must be square to be diagonalizable. :: |
| 6435 | |
| 6436 | sage: A = matrix(QQ, 3, 4) |
| 6437 | sage: A.is_diagonalizable() |
| 6438 | False |
| 6439 | |
| 6440 | The matrix must have entries from a field, |
| 6441 | and it must be an exact field. :: |
| 6442 | |
| 6443 | sage: A = matrix(ZZ, 4, range(16)) |
| 6444 | sage: A.is_diagonalizable() |
| 6445 | Traceback (most recent call last): |
| 6446 | ... |
| 6447 | ValueError: matrix entries must be from a field, not Integer Ring |
| 6448 | |
| 6449 | sage: A = matrix(RDF, 4, range(16)) |
| 6450 | sage: A.is_diagonalizable() |
| 6451 | Traceback (most recent call last): |
| 6452 | ... |
| 6453 | ValueError: base field must be exact, not Real Double Field |
| 6454 | |
| 6455 | AUTHOR: |
| 6456 | |
| 6457 | - Rob Beezer (2011-04-01) |
| 6458 | """ |
| 6459 | if not self.is_square(): |
| 6460 | return False |
| 6461 | if not base_field is None: |
| 6462 | self = self.change_ring(base_field) |
| 6463 | if not self.base_ring().is_exact(): |
| 6464 | raise ValueError('base field must be exact, not {0}'.format(self.base_ring())) |
| 6465 | if not self.base_ring().is_field(): |
| 6466 | raise ValueError('matrix entries must be from a field, not {0}'.format(self.base_ring())) |
| 6467 | |
| 6468 | evals = self.charpoly().roots() |
| 6469 | if sum([mult for (_,mult) in evals]) < self._nrows: |
| 6470 | raise RuntimeError('an eigenvalue of the matrix is not contained in {0}'.format(self.base_ring())) |
| 6471 | |
| 6472 | # Obtaining a generic minimal polynomial requires much more |
| 6473 | # computation with kernels and their dimensions than the following. |
| 6474 | # However, if a derived class has a fast minimal polynomial routine |
| 6475 | # then overriding this by checking for repeated factors might be faster. |
| 6476 | |
| 6477 | # check equality of algebraic multiplicity and geometric multiplicity |
| 6478 | for e, am in evals: |
| 6479 | gm = (self - e).right_kernel().dimension() |
| 6480 | if am != gm: |
| 6481 | return False |
| 6482 | return True |
| 6483 | |