LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
matrix_decomposition_eigen.cpp
浏览该文件的文档.
3
7#include <new>
8#include <optional>
9
10namespace LMCAS {
11using namespace detail::matrix_decomposition;
12namespace {
14 const JordanDecomposition& value, const MatrixNode& matrix,
15 ComputationContext& context, const std::string& operation) {
16 auto ap = matrix_multiply_checked(A, value.P, context);
17 if (!ap) { return Result<void>::failure(ap.error()); }
19 if (!pj) { return Result<void>::failure(pj.error()); }
20 auto left = std::dynamic_pointer_cast<const MatrixNode>(detail::node(ap.value()));
21 auto right = std::dynamic_pointer_cast<const MatrixNode>(detail::node(pj.value()));
22 if (!left || !right) {
24 "Jordan matrix product did not produce a matrix", operation);
25 }
26 for (std::size_t row = 0; row < matrix.rows(); ++row) {
27 for (std::size_t col = 0; col < matrix.cols(); ++col) {
28 auto difference = SymbolicExpr::add(detail::make_expression_ptr(left->get(row, col)),
29 SymbolicExpr::multiply(SymbolicExpr::number(-1),
30 detail::make_expression_ptr(right->get(row, col))));
31 auto proof = detail::classify_exact_zero(difference, context, operation);
32 if (!proof) { return Result<void>::failure(proof.error()); }
33 if (proof.value() != detail::ZeroProof::Zero) {
36 "Jordan intertwining identity is not proved at (" +
37 std::to_string(row) + "," + std::to_string(col) +
38 "): " + difference->to_string(), operation);
39 }
40 }
41 }
42 return Result<void>::success();
43}
44
46 const MatrixNode& j) {
47 return a.rows() == a.cols() && p.rows() == a.rows() && p.cols() == a.cols() &&
48 j.rows() == a.rows() && j.cols() == a.cols();
49}
50
53 const std::string operation = "jordan_form.verify";
54 auto a = std::dynamic_pointer_cast<const MatrixNode>(detail::node(A));
55 auto p = std::dynamic_pointer_cast<const MatrixNode>(detail::node(value.P));
56 auto j = std::dynamic_pointer_cast<const MatrixNode>(detail::node(value.J));
57 if (!a || !p || !j || !jordan_dimensions_match(*a, *p, *j)) {
59 "Jordan certificate has invalid dimensions", operation);
60 }
61 auto rank = matrix_rank_checked(value.P, context);
62 if (!rank) { return Result<void>::failure(rank.error()); }
63 if (rank.value() != a->rows()) {
65 "Jordan change of basis is singular", operation);
66 }
67 return verify_jordan_identity(A, value, *a, context, operation);
68}
69
76
79 auto matrix = validate_decomposition_matrix(A, true, context, "matrix.diagonalize");
80 if (!matrix) { return JordanDecompositionResult::failure(matrix.error()); }
81 if (auto diagonal = exact_diagonal_entries(matrix.value(), context)) {
82 return certified_jordan(A, {rectangular_diagonal_expr(matrix.value()->rows(),
83 matrix.value()->cols(), *diagonal), identity_matrix_expr(matrix.value()->rows())}, context);
84 }
86 if (!spaces) { return JordanDecompositionResult::failure(spaces.error()); }
87 const auto n = matrix.value()->rows();
88 std::size_t count = 0;
89 for (const auto& space : spaces.value()) { count += space.second.size(); }
90 if (count != n) {
92 "ordinary eigenspaces do not span the matrix", "matrix.diagonalize");
93 }
94 std::vector<std::vector<ExprPtr>> p(n, std::vector<ExprPtr>(n));
95 std::vector<std::vector<ExprPtr>> j(n,
96 std::vector<ExprPtr>(n, SymbolicExpr::number(0)));
97 std::size_t column = 0;
98 for (const auto& space : spaces.value()) {
99 for (const auto& vector : space.second) {
100 if (vector.size() != n) {
102 "eigenspace vector has invalid dimensions", "matrix.diagonalize");
103 }
104 for (std::size_t row = 0; row < n; ++row) { p[row][column] = vector[row]; }
105 j[column][column] = space.first;
106 ++column;
107 }
108 }
109 return certified_jordan(A,
110 {SymbolicExpr::matrix(j), SymbolicExpr::matrix(p)}, context);
111}
112
114 bool has_pos = false;
115 bool has_neg = false;
116 bool any_zero = false;
117 void include(int proved_sign) {
118 if (proved_sign > 0) has_pos = true;
119 else if (proved_sign < 0) has_neg = true;
120 else any_zero = true;
121 }
122 std::string classification() const {
123 if (has_pos && has_neg) { return "indefinite"; }
124 if (has_pos) { return any_zero ? "positive_semidefinite" : "positive_definite"; }
125 if (has_neg) { return any_zero ? "negative_semidefinite" : "negative_definite"; }
126 return any_zero ? "positive_semidefinite" : "unknown";
127 }
128};
129
130std::optional<int> symbolic_quadratic_sign(const ExprPtr& value,
133 if (!real || real.value() != Tribool::True) { return std::nullopt; }
134 auto zero = detail::classify_exact_zero(value, context, "matrix.quadratic_sign");
135 if (!zero) { return std::nullopt; }
136 if (zero.value() == detail::ZeroProof::Zero) { return 0; }
137 auto positive = detail::query_positive_value(detail::node(value), facts, context);
138 if (!positive) { return std::nullopt; }
139 if (positive.value() == Tribool::True) { return 1; }
140 auto negative_value = SymbolicExpr::multiply(SymbolicExpr::number(-1), value);
141 auto negative = detail::query_positive_value(detail::node(negative_value), facts, context);
142 if (!negative || negative.value() != Tribool::True) { return std::nullopt; }
143 return -1;
144}
145
146std::optional<int> quadratic_eigenvalue_sign(const ExprPtr& eigenvalue,
148 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
149 (context).limits().max_ast_nodes, "transform");
150 auto value = detail::make_expression_ptr(detail::normalize_node(detail::node(eigenvalue), transform_budget));
152 return *rational > Rational(0) ? 1 : (*rational < Rational(0) ? -1 : 0);
153 }
154 return symbolic_quadratic_sign(value, facts, context);
155}
156
157bool is_defective_upper_pair(const std::vector<std::vector<Rational>>& matrix) {
158 return matrix.size() == 2 && matrix[0].size() == 2 &&
159 matrix[0][0] == matrix[1][1] && matrix[1][0] == Rational(0) &&
160 matrix[0][1] != Rational(0);
161}
162
163}
164
167 const std::string operation = "jordan_form";
168 auto matrix = validate_decomposition_matrix(A, true, context, operation);
169 if (!matrix) { return JordanDecompositionResult::failure(matrix.error()); }
170 auto budget = context.consume_steps(matrix.value()->rows() * matrix.value()->cols() * 48 + 32,
171 operation);
172 if (!budget) { return JordanDecompositionResult::failure(budget.error()); }
173 try {
174 if (auto diagonal = exact_diagonal_entries(matrix.value(), context)) {
175 return certified_jordan(A, {rectangular_diagonal_expr(
176 matrix.value()->rows(), matrix.value()->cols(), *diagonal),
177 identity_matrix_expr(matrix.value()->rows())}, context);
178 }
179 auto exact = exact_rational_matrix(matrix.value(), context);
180 if (exact && is_defective_upper_pair(*exact)) {
181 auto zero = SymbolicExpr::number(0);
182 auto one = SymbolicExpr::number(1);
183 auto lambda = exact_number_expr((*exact)[0][0]);
184 auto j = SymbolicExpr::matrix({{lambda, one}, {zero, lambda}});
185 auto p = SymbolicExpr::matrix({{exact_number_expr((*exact)[0][1]), zero}, {zero, one}});
186 return certified_jordan(A, {std::move(j), std::move(p)}, context);
187 }
188 return diagonalization_checked(A, context);
189 } catch (const std::bad_alloc&) {
191 "allocation failed while calculating Jordan form", operation);
192 } catch (const std::exception& ex) {
194 }
195}
196
201
204 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
205 (context).limits().max_ast_nodes, "transform");
206
207 auto decomposition = diagonalization_checked(A, context);
208 if (!decomposition) return SymbolicExpr::exp(A);
209 auto inverse = matrix_inverse_checked(decomposition.value().P, context);
210 if (!inverse) return SymbolicExpr::exp(A);
211 auto diagonal = std::dynamic_pointer_cast<const MatrixNode>(detail::node(decomposition.value().J));
212 const auto n = diagonal->rows();
213 std::vector<std::vector<ExprPtr>> values(n, std::vector<ExprPtr>(n, SymbolicExpr::number(0)));
214 for (std::size_t i = 0; i < n; ++i)
215 values[i][i] = SymbolicExpr::exp(detail::make_expression_ptr(diagonal->get(i, i)));
216 return detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::multiply(decomposition.value().P,
217 SymbolicExpr::multiply(SymbolicExpr::matrix(values), inverse.value()))), transform_budget));
218}
219
222 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
223 (context).limits().max_ast_nodes, "transform");
224
225 auto decomposition = diagonalization_checked(A, context);
226 if (!decomposition) return nullptr;
227 auto inverse = matrix_inverse_checked(decomposition.value().P, context);
228 if (!inverse) return nullptr;
229 auto diagonal = std::dynamic_pointer_cast<const MatrixNode>(detail::node(decomposition.value().J));
230 const auto n = diagonal->rows();
231 AssumptionContext assumptions;
232 detail::AssumptionFacts facts(assumptions);
233 std::vector<std::vector<ExprPtr>> values(n, std::vector<ExprPtr>(n, SymbolicExpr::number(0)));
234 for (std::size_t i = 0; i < n; ++i) {
235 auto positive = detail::query_positive_value(diagonal->get(i, i), facts, context);
236 if (!positive || positive.value() != Tribool::True) return nullptr;
237 values[i][i] = SymbolicExpr::ln(detail::make_expression_ptr(diagonal->get(i, i)));
238 }
239 return detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::multiply(decomposition.value().P,
240 SymbolicExpr::multiply(SymbolicExpr::matrix(values), inverse.value()))), transform_budget));
241}
242
243std::string classify_quadratic_form(const ExprPtr& A) {
245 auto eigenvalues = matrix_eigenvalues_checked(A, context);
246 if (!eigenvalues || eigenvalues.value().empty()) { return "unknown"; }
247 AssumptionContext assumptions;
248 detail::AssumptionFacts facts(assumptions);
249 QuadraticSigns signs;
250 for (const auto& eigenvalue : eigenvalues.value()) {
251 auto sign = quadratic_eigenvalue_sign(eigenvalue, facts, context);
252 if (!sign) { return "unknown"; }
253 signs.include(*sign);
254 }
255 return signs.classification();
256}
257}
AssumptionContext class with scoped push/pop management.
矩阵节点,支持稠密和稀疏两种存储方式。
size_t cols() const noexcept
size_t rows() const noexcept
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(T value)
Result< void > verify_jordan_decomposition(const ExprPtr &A, const JordanDecomposition &value, ComputationContext &context)
std::optional< int > symbolic_quadratic_sign(const ExprPtr &value, const detail::AssumptionFacts &facts, ComputationContext &context)
JordanDecompositionResult certified_jordan(const ExprPtr &A, JordanDecomposition value, ComputationContext &context)
Result< void > verify_jordan_identity(const ExprPtr &A, const JordanDecomposition &value, const MatrixNode &matrix, ComputationContext &context, const std::string &operation)
JordanDecompositionResult diagonalization_checked(const ExprPtr &A, ComputationContext &context)
bool is_defective_upper_pair(const std::vector< std::vector< Rational > > &matrix)
bool jordan_dimensions_match(const MatrixNode &a, const MatrixNode &p, const MatrixNode &j)
std::optional< int > quadratic_eigenvalue_sign(const ExprPtr &eigenvalue, const detail::AssumptionFacts &facts, ComputationContext &context)
std::shared_ptr< SymbolicExpr > identity_matrix_expr(std::size_t n)
std::shared_ptr< SymbolicExpr > exact_number_expr(const Rational &value)
std::optional< Rational > exact_rational_expr(const std::shared_ptr< const SymbolicNode > &node)
std::optional< std::vector< Rational > > exact_diagonal_entries(const std::shared_ptr< const MatrixNode > &matrix, ComputationContext &context)
std::shared_ptr< SymbolicExpr > rectangular_diagonal_expr(std::size_t rows, std::size_t cols, const std::vector< Rational > &diagonal)
Result< std::shared_ptr< const MatrixNode > > validate_decomposition_matrix(const std::shared_ptr< SymbolicExpr > &A, bool require_square, ComputationContext &context, const std::string &operation)
std::optional< std::vector< std::vector< Rational > > > exact_rational_matrix(const std::shared_ptr< const MatrixNode > &matrix, ComputationContext &context)
Result< ZeroProof > classify_exact_zero(const ExprPtr &expression, ComputationContext &context, const std::string &operation)
std::shared_ptr<::LMCAS::SymbolicExpr > make_expression_ptr(SymbolicNodePtr root)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
Result< Tribool > query_real_value(const Node &node, const FactsQuery &facts, ComputationContext &context)
SymbolicNodePtr normalize_node(const SymbolicNodePtr &node, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
Result< std::vector< std::pair< ExprPtr, std::vector< std::vector< ExprPtr > > > > > matrix_eigenspaces_checked(const ExprPtr &matrix, ComputationContext &context)
Result< Tribool > query_positive_value(const Node &node, const FactsQuery &facts, ComputationContext &context)
MatrixEigenvalueResult matrix_eigenvalues_checked(const std::shared_ptr< SymbolicExpr > &A, ComputationContext &context)
JordanDecompositionResult jordan_form_checked(const ExprPtr &A, ComputationContext &context)
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
ExprResult real(const ExprPtr &expression, ComputationContext &context)
@ True
Property definitely holds
Result< JordanDecomposition > JordanDecompositionResult
ExpressionResult matrix_inverse_checked(const std::shared_ptr< SymbolicExpr > &A, ComputationContext &context)
__attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) std const VectorField const std::string const std::shared_ptr< SymbolicExpr > & a
ExpressionResult matrix_multiply_checked(const std::shared_ptr< SymbolicExpr > &A, const std::shared_ptr< SymbolicExpr > &B, ComputationContext &context)
std::shared_ptr< SymbolicExpr > ExprPtr
const ExprPtr & right
const std::string const ExprPtr & value
std::string classify_quadratic_form(const ExprPtr &A)
MatrixRankResult matrix_rank_checked(const std::shared_ptr< SymbolicExpr > &A, ComputationContext &context)
ExprResult rational(const Rational &value)
ExprPtr matrix_exp(const ExprPtr &A)
ExprPtr matrix_log(const ExprPtr &A)