@@ -6390,10 +6390,21 @@ cdef class Matrix(Matrix1):
6390
6390
self.cache('eigenvalues', eigenvalues)
6391
6391
return eigenvalues
6392
6392
6393
- def eigenvectors_left(self,extend=True):
6393
+ def eigenvectors_left(self, other=None, *, extend=True):
6394
6394
r"""
6395
6395
Compute the left eigenvectors of a matrix.
6396
6396
6397
+ INPUT:
6398
+
6399
+ - ``other`` -- a square matrix `B` (default: ``None``) in a generalized
6400
+ eigenvalue problem; if ``None``, an ordinary eigenvalue problem is
6401
+ solved (currently supported only if the base ring of ``self`` is
6402
+ ``RDF`` or ``CDF``)
6403
+
6404
+ - ``extend`` -- boolean (default: ``True``)
6405
+
6406
+ OUTPUT:
6407
+
6397
6408
For each distinct eigenvalue, returns a list of the form (e,V,n)
6398
6409
where e is the eigenvalue, V is a list of eigenvectors forming a
6399
6410
basis for the corresponding left eigenspace, and n is the algebraic
@@ -6402,8 +6413,9 @@ cdef class Matrix(Matrix1):
6402
6413
If the option extend is set to False, then only the eigenvalues that
6403
6414
live in the base ring are considered.
6404
6415
6405
- EXAMPLES: We compute the left eigenvectors of a `3\times 3`
6406
- rational matrix.
6416
+ EXAMPLES:
6417
+
6418
+ We compute the left eigenvectors of a `3\times 3` rational matrix.
6407
6419
6408
6420
::
6409
6421
@@ -6436,7 +6448,37 @@ cdef class Matrix(Matrix1):
6436
6448
(0, 0, 1)
6437
6449
], 1)]
6438
6450
6451
+ TESTS::
6452
+
6453
+ sage: A = matrix(QQ, [[1, 2], [3, 4]])
6454
+ sage: B = matrix(QQ, [[1, 1], [0, 1]])
6455
+ sage: A.eigenvectors_left(B)
6456
+ Traceback (most recent call last):
6457
+ ...
6458
+ NotImplementedError: generalized eigenvector decomposition is
6459
+ implemented for RDF and CDF, but not for Rational Field
6460
+
6461
+ Check the deprecation::
6462
+
6463
+ sage: matrix(QQ, [[1, 2], [3, 4]]).eigenvectors_left(False)
6464
+ doctest:...: DeprecationWarning: "extend" should be used as keyword argument
6465
+ See https://trac.sagemath.org/29243 for details.
6466
+ []
6439
6467
"""
6468
+ if other is not None:
6469
+ if isinstance(other, bool):
6470
+ # for backward compatibility
6471
+ from sage.misc.superseded import deprecation
6472
+ deprecation(29243,
6473
+ '"extend" should be used as keyword argument')
6474
+ extend = other
6475
+ other = None
6476
+ else:
6477
+ raise NotImplementedError('generalized eigenvector '
6478
+ 'decomposition is implemented '
6479
+ 'for RDF and CDF, but not for %s'
6480
+ % self.base_ring())
6481
+
6440
6482
x = self.fetch('eigenvectors_left')
6441
6483
if not x is None:
6442
6484
return x
@@ -6476,10 +6518,21 @@ cdef class Matrix(Matrix1):
6476
6518
6477
6519
left_eigenvectors = eigenvectors_left
6478
6520
6479
- def eigenvectors_right(self, extend=True):
6521
+ def eigenvectors_right(self, other=None, *, extend=True):
6480
6522
r"""
6481
6523
Compute the right eigenvectors of a matrix.
6482
6524
6525
+ INPUT:
6526
+
6527
+ - ``other`` -- a square matrix `B` (default: ``None``) in a generalized
6528
+ eigenvalue problem; if ``None``, an ordinary eigenvalue problem is
6529
+ solved (currently supported only if the base ring of ``self`` is
6530
+ ``RDF`` or ``CDF``)
6531
+
6532
+ - ``extend`` -- boolean (default: ``True``)
6533
+
6534
+ OUTPUT:
6535
+
6483
6536
For each distinct eigenvalue, returns a list of the form (e,V,n)
6484
6537
where e is the eigenvalue, V is a list of eigenvectors forming a
6485
6538
basis for the corresponding right eigenspace, and n is the
@@ -6488,8 +6541,9 @@ cdef class Matrix(Matrix1):
6488
6541
closure of the base field where this is implemented; otherwise
6489
6542
it will restrict to eigenvalues in the base field.
6490
6543
6491
- EXAMPLES: We compute the right eigenvectors of a
6492
- `3\times 3` rational matrix.
6544
+ EXAMPLES:
6545
+
6546
+ We compute the right eigenvectors of a `3\times 3` rational matrix.
6493
6547
6494
6548
::
6495
6549
@@ -6511,17 +6565,57 @@ cdef class Matrix(Matrix1):
6511
6565
sage: delta = eval*evec - A*evec
6512
6566
sage: abs(abs(delta)) < 1e-10
6513
6567
True
6568
+
6569
+ TESTS::
6570
+
6571
+ sage: A = matrix(QQ, [[1, 2], [3, 4]])
6572
+ sage: B = matrix(QQ, [[1, 1], [0, 1]])
6573
+ sage: A.eigenvectors_right(B)
6574
+ Traceback (most recent call last):
6575
+ ...
6576
+ NotImplementedError: generalized eigenvector decomposition is
6577
+ implemented for RDF and CDF, but not for Rational Field
6514
6578
"""
6515
- return self.transpose().eigenvectors_left(extend=extend)
6579
+ return self.transpose().eigenvectors_left(other=other, extend=extend)
6516
6580
6517
6581
right_eigenvectors = eigenvectors_right
6518
6582
6519
- def eigenmatrix_left(self):
6583
+ def eigenmatrix_left(self, other=None ):
6520
6584
r"""
6521
- Return matrices D and P, where D is a diagonal matrix of
6522
- eigenvalues and P is the corresponding matrix where the rows are
6523
- corresponding eigenvectors (or zero vectors) so that P\*self =
6524
- D\*P.
6585
+ Return matrices `D` and `P`, where `D` is a diagonal matrix of
6586
+ eigenvalues and the rows of `P` are corresponding eigenvectors
6587
+ (or zero vectors).
6588
+
6589
+ INPUT:
6590
+
6591
+ - ``other`` -- a square matrix `B` (default: ``None``) in a generalized
6592
+ eigenvalue problem; if ``None``, an ordinary eigenvalue problem is
6593
+ solved
6594
+
6595
+ OUTPUT:
6596
+
6597
+ If ``self`` is a square matrix `A`, then the output is a diagonal
6598
+ matrix `D` and a matrix `P` such that
6599
+
6600
+ .. MATH::
6601
+
6602
+ P A = D P,
6603
+
6604
+ where the rows of `P` are eigenvectors of `A` and the diagonal entries
6605
+ of `D` are the corresponding eigenvalues.
6606
+
6607
+ If a matrix `B` is passed as optional argument, the output is a
6608
+ solution to the generalized eigenvalue problem such that
6609
+
6610
+ .. MATH::
6611
+
6612
+ P A = D P B.
6613
+
6614
+ The ordinary eigenvalue problem is equivalent to the generalized one if
6615
+ `B` is the identity matrix.
6616
+
6617
+ The generalized eigenvector decomposition is currently only implemented
6618
+ for matrices over ``RDF`` and ``CDF``.
6525
6619
6526
6620
EXAMPLES::
6527
6621
@@ -6541,14 +6635,14 @@ cdef class Matrix(Matrix1):
6541
6635
sage: P*A == D*P
6542
6636
True
6543
6637
6544
- Because P is invertible, A is diagonalizable.
6638
+ Because `P` is invertible, `A` is diagonalizable.
6545
6639
6546
6640
::
6547
6641
6548
6642
sage: A == (~P)*D*P
6549
6643
True
6550
6644
6551
- The matrix P may contain zero rows corresponding to eigenvalues for
6645
+ The matrix `P` may contain zero rows corresponding to eigenvalues for
6552
6646
which the algebraic multiplicity is greater than the geometric
6553
6647
multiplicity. In these cases, the matrix is not diagonalizable.
6554
6648
@@ -6558,7 +6652,6 @@ cdef class Matrix(Matrix1):
6558
6652
[2 1 0]
6559
6653
[0 2 1]
6560
6654
[0 0 2]
6561
- sage: A = jordan_block(2,3)
6562
6655
sage: D, P = A.eigenmatrix_left()
6563
6656
sage: D
6564
6657
[2 0 0]
@@ -6571,6 +6664,42 @@ cdef class Matrix(Matrix1):
6571
6664
sage: P*A == D*P
6572
6665
True
6573
6666
6667
+ A generalized eigenvector decomposition::
6668
+
6669
+ sage: A = matrix(RDF, [[1, -2], [3, 4]])
6670
+ sage: B = matrix(RDF, [[0, 7], [2, -3]])
6671
+ sage: D, P = A.eigenmatrix_left(B)
6672
+ sage: (P * A - D * P * B).norm() < 1e-14
6673
+ True
6674
+
6675
+ The matrix `B` in a generalized eigenvalue problem may be singular::
6676
+
6677
+ sage: A = matrix.identity(CDF, 2)
6678
+ sage: B = matrix(CDF, [[2, 1+I], [4, 2+2*I]])
6679
+ sage: D, P = A.eigenmatrix_left(B)
6680
+ sage: D.diagonal() # tol 1e-14
6681
+ [0.2 - 0.1*I, +infinity]
6682
+
6683
+ In this case, we can still verify the eigenvector equation for the
6684
+ first eigenvalue and first eigenvector::
6685
+
6686
+ sage: l = D[0, 0]
6687
+ sage: v = P[0, :]
6688
+ sage: (v * A - l * v * B).norm() < 1e-14
6689
+ True
6690
+
6691
+ The second eigenvector is contained in the left kernel of `B`::
6692
+
6693
+ sage: (P[1, :] * B).norm() < 1e-14
6694
+ True
6695
+
6696
+ .. SEEALSO::
6697
+
6698
+ :meth:`eigenvalues`,
6699
+ :meth:`eigenvectors_left`,
6700
+ :meth:`.Matrix_double_dense.eigenvectors_left`,
6701
+ :meth:`eigenmatrix_right`.
6702
+
6574
6703
TESTS:
6575
6704
6576
6705
For matrices with floating point entries, some platforms will
@@ -6630,7 +6759,7 @@ cdef class Matrix(Matrix1):
6630
6759
"""
6631
6760
from sage.misc.flatten import flatten
6632
6761
from sage.matrix.constructor import diagonal_matrix, matrix
6633
- evecs = self.eigenvectors_left()
6762
+ evecs = self.eigenvectors_left(other=other )
6634
6763
D = diagonal_matrix(flatten([[e[0]]*e[2] for e in evecs]))
6635
6764
rows = []
6636
6765
for e in evecs:
@@ -6647,12 +6776,42 @@ cdef class Matrix(Matrix1):
6647
6776
6648
6777
left_eigenmatrix = eigenmatrix_left
6649
6778
6650
- def eigenmatrix_right(self):
6779
+ def eigenmatrix_right(self, other=None ):
6651
6780
r"""
6652
- Return matrices D and P, where D is a diagonal matrix of
6653
- eigenvalues and P is the corresponding matrix where the columns are
6654
- corresponding eigenvectors (or zero vectors) so that self\*P =
6655
- P\*D.
6781
+ Return matrices `D` and `P`, where `D` is a diagonal matrix of
6782
+ eigenvalues and the columns of `P` are corresponding eigenvectors
6783
+ (or zero vectors).
6784
+
6785
+ INPUT:
6786
+
6787
+ - ``other`` -- a square matrix `B` (default: ``None``) in a generalized
6788
+ eigenvalue problem; if ``None``, an ordinary eigenvalue problem is
6789
+ solved
6790
+
6791
+ OUTPUT:
6792
+
6793
+ If ``self`` is a square matrix `A`, then the output is a diagonal
6794
+ matrix `D` and a matrix `P` such that
6795
+
6796
+ .. MATH::
6797
+
6798
+ A P = P D,
6799
+
6800
+ where the columns of `P` are eigenvectors of `A` and the diagonal
6801
+ entries of `D` are the corresponding eigenvalues.
6802
+
6803
+ If a matrix `B` is passed as optional argument, the output is a
6804
+ solution to the generalized eigenvalue problem such that
6805
+
6806
+ .. MATH::
6807
+
6808
+ A P = B P D.
6809
+
6810
+ The ordinary eigenvalue problem is equivalent to the generalized one if
6811
+ `B` is the identity matrix.
6812
+
6813
+ The generalized eigenvector decomposition is currently only implemented
6814
+ for matrices over ``RDF`` and ``CDF``.
6656
6815
6657
6816
EXAMPLES::
6658
6817
@@ -6672,14 +6831,14 @@ cdef class Matrix(Matrix1):
6672
6831
sage: A*P == P*D
6673
6832
True
6674
6833
6675
- Because P is invertible, A is diagonalizable.
6834
+ Because `P` is invertible, `A` is diagonalizable.
6676
6835
6677
6836
::
6678
6837
6679
6838
sage: A == P*D*(~P)
6680
6839
True
6681
6840
6682
- The matrix P may contain zero columns corresponding to eigenvalues
6841
+ The matrix `P` may contain zero columns corresponding to eigenvalues
6683
6842
for which the algebraic multiplicity is greater than the geometric
6684
6843
multiplicity. In these cases, the matrix is not diagonalizable.
6685
6844
@@ -6689,7 +6848,6 @@ cdef class Matrix(Matrix1):
6689
6848
[2 1 0]
6690
6849
[0 2 1]
6691
6850
[0 0 2]
6692
- sage: A = jordan_block(2,3)
6693
6851
sage: D, P = A.eigenmatrix_right()
6694
6852
sage: D
6695
6853
[2 0 0]
@@ -6702,6 +6860,42 @@ cdef class Matrix(Matrix1):
6702
6860
sage: A*P == P*D
6703
6861
True
6704
6862
6863
+ A generalized eigenvector decomposition::
6864
+
6865
+ sage: A = matrix(RDF, [[1, -2], [3, 4]])
6866
+ sage: B = matrix(RDF, [[0, 7], [2, -3]])
6867
+ sage: D, P = A.eigenmatrix_right(B)
6868
+ sage: (A * P - B * P * D).norm() < 1e-14
6869
+ True
6870
+
6871
+ The matrix `B` in a generalized eigenvalue problem may be singular::
6872
+
6873
+ sage: A = matrix.identity(RDF, 2)
6874
+ sage: B = matrix(RDF, [[3, 5], [6, 10]])
6875
+ sage: D, P = A.eigenmatrix_right(B); D # tol 1e-14
6876
+ [0.07692307692307694 0.0]
6877
+ [ 0.0 +infinity]
6878
+
6879
+ In this case, we can still verify the eigenvector equation for the
6880
+ first eigenvalue and first eigenvector::
6881
+
6882
+ sage: l = D[0, 0]
6883
+ sage: v = P[:, 0]
6884
+ sage: (A * v - B * v * l).norm() < 1e-14
6885
+ True
6886
+
6887
+ The second eigenvector is contained in the right kernel of `B`::
6888
+
6889
+ sage: (B * P[:, 1]).norm() < 1e-14
6890
+ True
6891
+
6892
+ .. SEEALSO::
6893
+
6894
+ :meth:`eigenvalues`,
6895
+ :meth:`eigenvectors_right`,
6896
+ :meth:`.Matrix_double_dense.eigenvectors_right`,
6897
+ :meth:`eigenmatrix_left`.
6898
+
6705
6899
TESTS:
6706
6900
6707
6901
For matrices with floating point entries, some platforms will
@@ -6744,7 +6938,8 @@ cdef class Matrix(Matrix1):
6744
6938
True
6745
6939
6746
6940
"""
6747
- D,P = self.transpose().eigenmatrix_left()
6941
+ D,P = self.transpose().eigenmatrix_left(None if other is None
6942
+ else other.transpose())
6748
6943
return D,P.transpose()
6749
6944
6750
6945
right_eigenmatrix = eigenmatrix_right
0 commit comments