mathhook_core/matrices/eigenvalues.rs
1//! Eigenvalue and eigenvector computation
2//!
3//! This module provides methods for computing eigenvalues and eigenvectors
4//! of matrices, including both real and complex cases, characteristic polynomials,
5//! and matrix functions using eigendecomposition.
6
7pub mod characteristic;
8pub mod computation;
9pub mod eigenvalues_tests;
10pub mod power_methods;
11
12use crate::core::expression::Expression;
13use crate::core::symbol::Symbol;
14use crate::matrices::eigenvalues::characteristic::CharacteristicPolynomial;
15use crate::matrices::types::*;
16use crate::matrices::unified::Matrix;
17use crate::simplify::Simplify;
18
19/// Eigenvalue and eigenvector operations trait
20///
21/// This trait provides a unified interface for all eigenvalue-related computations.
22/// Methods return Options to handle cases where computation is not possible.
23pub trait EigenOperations {
24 /// Compute eigenvalues and eigenvectors
25 ///
26 /// Returns eigenvalues and corresponding eigenvectors for real matrices.
27 /// For matrices with complex eigenvalues, use `complex_eigen_decomposition`.
28 ///
29 /// # Examples
30 ///
31 /// ```
32 /// use mathhook_core::matrices::Matrix;
33 /// use mathhook_core::matrices::eigenvalues::EigenOperations;
34 /// use mathhook_core::Expression;
35 ///
36 /// let matrix = Matrix::diagonal(vec![
37 /// Expression::integer(2),
38 /// Expression::integer(3)
39 /// ]);
40 ///
41 /// let eigen = matrix.eigen_decomposition().unwrap();
42 /// assert_eq!(eigen.eigenvalues.len(), 2);
43 /// ```
44 fn eigen_decomposition(&self) -> Option<EigenDecomposition>;
45
46 /// Compute complex eigenvalues and eigenvectors
47 ///
48 /// Handles matrices that may have complex eigenvalues and eigenvectors.
49 fn complex_eigen_decomposition(&self) -> Option<ComplexEigenDecomposition>;
50
51 /// Compute only eigenvalues (faster than full decomposition)
52 ///
53 /// # Examples
54 ///
55 /// ```
56 /// use mathhook_core::matrices::Matrix;
57 /// use mathhook_core::matrices::eigenvalues::EigenOperations;
58 /// use mathhook_core::Expression;
59 ///
60 /// let matrix = Matrix::diagonal(vec![
61 /// Expression::integer(2),
62 /// Expression::integer(3)
63 /// ]);
64 ///
65 /// let eigenvalues = matrix.eigenvalues();
66 /// assert_eq!(eigenvalues.len(), 2);
67 /// assert_eq!(eigenvalues[0], Expression::integer(2));
68 /// assert_eq!(eigenvalues[1], Expression::integer(3));
69 /// ```
70 fn eigenvalues(&self) -> Vec<Expression>;
71
72 /// Compute characteristic polynomial det(A - λI)
73 ///
74 /// # Examples
75 ///
76 /// ```
77 /// use mathhook_core::matrices::Matrix;
78 /// use mathhook_core::matrices::eigenvalues::EigenOperations;
79 /// use mathhook_core::Expression;
80 ///
81 /// let matrix = Matrix::diagonal(vec![
82 /// Expression::integer(2),
83 /// Expression::integer(3)
84 /// ]);
85 ///
86 /// let poly = matrix.characteristic_polynomial();
87 /// assert_eq!(poly.degree(), 2);
88 /// ```
89 fn characteristic_polynomial(&self) -> CharacteristicPolynomial;
90
91 /// Get the trace (sum of eigenvalues)
92 fn trace(&self) -> Expression;
93
94 /// Get the determinant (product of eigenvalues)
95 fn determinant_via_eigenvalues(&self) -> Expression;
96
97 /// Check if matrix is diagonalizable
98 fn is_diagonalizable(&self) -> bool;
99
100 /// Compute matrix power using eigendecomposition
101 ///
102 /// For diagonalizable matrices, computes A^n = P D^n P^(-1)
103 ///
104 /// # Examples
105 ///
106 /// ```
107 /// use mathhook_core::matrices::Matrix;
108 /// use mathhook_core::matrices::eigenvalues::EigenOperations;
109 /// use mathhook_core::Expression;
110 ///
111 /// let matrix = Matrix::diagonal(vec![
112 /// Expression::integer(2),
113 /// Expression::integer(3)
114 /// ]);
115 ///
116 /// let power = matrix.matrix_power_eigen(3).unwrap();
117 /// // Returns diag([8, 27])
118 /// ```
119 fn matrix_power_eigen(&self, n: i64) -> Option<Matrix>;
120
121 /// Compute matrix exponential using eigendecomposition
122 fn matrix_exponential(&self) -> Option<Matrix>;
123
124 /// Compute matrix logarithm using eigendecomposition
125 fn matrix_logarithm(&self) -> Option<Matrix>;
126
127 /// Compute matrix square root using eigendecomposition
128 fn matrix_sqrt(&self) -> Option<Matrix>;
129
130 /// Check if matrix is nilpotent
131 fn is_nilpotent(&self) -> bool;
132}
133
134/// Implementation of EigenOperations trait for Matrix
135impl EigenOperations for Matrix {
136 fn eigen_decomposition(&self) -> Option<EigenDecomposition> {
137 // Call inherent method from computation module
138 // Rust resolves this correctly because it's defined as impl Matrix {...}
139 Matrix::eigen_decomposition(self)
140 }
141
142 fn complex_eigen_decomposition(&self) -> Option<ComplexEigenDecomposition> {
143 // Call inherent method from computation module
144 Matrix::complex_eigen_decomposition(self)
145 }
146
147 fn eigenvalues(&self) -> Vec<Expression> {
148 // Call inherent method from computation module
149 Matrix::eigenvalues(self)
150 }
151
152 fn characteristic_polynomial(&self) -> CharacteristicPolynomial {
153 // Construct characteristic polynomial from eigenvalues
154 // For a matrix with eigenvalues λ₁, λ₂, ..., λₙ:
155 // char_poly(λ) = (λ - λ₁)(λ - λ₂)...(λ - λₙ)
156 let eigenvals = Matrix::eigenvalues(self);
157 let lambda = Symbol::scalar("lambda");
158
159 if eigenvals.is_empty() {
160 return CharacteristicPolynomial::new(vec![Expression::integer(0)], lambda);
161 }
162
163 // Start with (λ - λ₁)
164 let mut poly = CharacteristicPolynomial::new(
165 vec![
166 Expression::mul(vec![Expression::integer(-1), eigenvals[0].clone()]),
167 Expression::integer(1),
168 ],
169 lambda.clone(),
170 );
171
172 // Multiply by (λ - λᵢ) for remaining eigenvalues
173 for eigenval in eigenvals.iter().skip(1) {
174 let factor = CharacteristicPolynomial::new(
175 vec![
176 Expression::mul(vec![Expression::integer(-1), eigenval.clone()]),
177 Expression::integer(1),
178 ],
179 lambda.clone(),
180 );
181 poly = poly.multiply(&factor);
182 }
183
184 poly
185 }
186
187 fn trace(&self) -> Expression {
188 // Call inherent method from unified/operations.rs
189 // Note: Matrix has a trace() inherent method, not from this trait
190 Matrix::trace(self)
191 }
192
193 fn determinant_via_eigenvalues(&self) -> Expression {
194 // Determinant equals product of eigenvalues
195 let eigenvals = Matrix::eigenvalues(self);
196 if eigenvals.is_empty() {
197 Expression::integer(0)
198 } else {
199 Expression::mul(eigenvals).simplify()
200 }
201 }
202
203 fn is_diagonalizable(&self) -> bool {
204 // Call inherent method from computation module
205 Matrix::is_diagonalizable(self)
206 }
207
208 fn matrix_power_eigen(&self, n: i64) -> Option<Matrix> {
209 // Call inherent method from power_methods module
210 Matrix::matrix_power_eigen(self, n)
211 }
212
213 fn matrix_exponential(&self) -> Option<Matrix> {
214 // Call inherent method from power_methods module
215 // Note: The inherent method is named matrix_exponential_eigen()
216 Matrix::matrix_exponential_eigen(self)
217 }
218
219 fn matrix_logarithm(&self) -> Option<Matrix> {
220 // Call inherent method from power_methods module
221 // Note: The inherent method is named matrix_logarithm_eigen()
222 Matrix::matrix_logarithm_eigen(self)
223 }
224
225 fn matrix_sqrt(&self) -> Option<Matrix> {
226 // Call inherent method from power_methods module
227 // Note: The inherent method is named matrix_sqrt_eigen()
228 Matrix::matrix_sqrt_eigen(self)
229 }
230
231 fn is_nilpotent(&self) -> bool {
232 // Matrix is nilpotent if all eigenvalues are zero
233 let eigenvals = Matrix::eigenvalues(self);
234 eigenvals.iter().all(|val| val.is_zero())
235 }
236}