LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
parametric_solver.cpp
浏览该文件的文档.
6#include <algorithm>
7#include <set>
8#include <optional>
9
10namespace LMCAS {
11
12static std::shared_ptr<SymbolicExpr> extract_coefficient(
13 const std::shared_ptr<SymbolicExpr>& expr,
14 const std::string& var, ComputationContext& context)
15{
16
17 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
18 (context).limits().max_ast_nodes, "transform");
19
20 return detail::make_expression_ptr(detail::differentiate_node(detail::node(expr), var, transform_budget));
21}
22
23static std::shared_ptr<SymbolicExpr> extract_constant_term(
24 const std::shared_ptr<SymbolicExpr>& expr,
25 const std::vector<std::string>& unknowns, ComputationContext& context)
26{
27
28 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
29 (context).limits().max_ast_nodes, "transform");
30
31
32 auto constant = expr;
33 for (const auto& var : unknowns) {
34 constant = detail::make_expression_ptr(detail::substitute_node(detail::node(constant), var, detail::node(SymbolicExpr::number(0)), transform_budget));
35 }
36 return detail::make_expression_ptr(detail::normalize_node(detail::node(constant), transform_budget));
37}
38
39bool ParametricSolver::is_linear_in_unknowns(
40 const std::vector<std::shared_ptr<SymbolicExpr>>& equations,
41 const std::vector<std::string>& unknowns, ComputationContext& context)
42{
43
44 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
45 (context).limits().max_ast_nodes, "transform");
46 for (const auto& eq : equations) {
47 for (const auto& var : unknowns) {
48
49 auto first_deriv = detail::make_expression_ptr(detail::differentiate_node(detail::node(eq), var, transform_budget));
50 auto second_deriv = detail::make_expression_ptr(detail::differentiate_node(detail::node(first_deriv), var, transform_budget));
51 if (!second_deriv->is_zero()) {
52 return false;
53 }
54
55 for (const auto& other_var : unknowns) {
56 if (other_var == var) {
57 continue;
58 }
59 auto mixed = detail::make_expression_ptr(detail::differentiate_node(detail::node(first_deriv), other_var, transform_budget));
60 if (!mixed->is_zero()) {
61 return false;
62 }
63 }
64 }
65 }
66 return true;
67}
68
69using ParametricMatrix = std::vector<std::vector<std::shared_ptr<SymbolicExpr>>>;
70using ParametricVector = std::vector<std::shared_ptr<SymbolicExpr>>;
71
72static bool parametric_residual_zero(const std::shared_ptr<SymbolicExpr>& expression,
74 auto residual = check_zero_residual(expression, context);
75 if (!residual) throw residual.error();
76 if (std::holds_alternative<ProvedZeroResidual>(residual.value())) return true;
78 "parameter coefficient or constraint is undecided", "solve.parametric"};
79}
80
81static Tribool parametric_nonzero_value(const std::shared_ptr<SymbolicExpr>& expression,
83 std::optional<detail::AssumptionFacts> assumptions;
84 if (context.assumptions()) assumptions.emplace(*context.assumptions());
85 const FactsQuery& facts = assumptions ? static_cast<const FactsQuery&>(*assumptions) : detail::no_facts();
86 auto nonzero = detail::query_nonzero_value(detail::node(expression), facts, Domain::Complex, context);
87 if (!nonzero) throw nonzero.error();
88 return nonzero.value();
89}
90
91static bool parametric_zero(const std::shared_ptr<SymbolicExpr>& expression,
93 if (expression->is_zero()) return true;
94 const auto nonzero = parametric_nonzero_value(expression, context);
95 if (nonzero == Tribool::True) return false;
96 if (nonzero == Tribool::False) return true;
97 return parametric_residual_zero(expression, context);
98}
99
100static size_t find_parametric_pivot(ParametricMatrix& matrix, size_t row, size_t column,
102 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
103 (context).limits().max_ast_nodes, "transform");
104 while (row < matrix.size()) {
105 auto simplified = detail::make_expression_ptr(detail::normalize_node(detail::node(matrix[row][column]), transform_budget));
106 matrix[row][column] = simplified;
107 if (!parametric_zero(simplified, context)) {
108 break;
109 }
110 ++row;
111 }
112 return row;
113}
114
116 size_t current_row, size_t col, size_t n, ComputationContext& context) {
117
118 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
119 (context).limits().max_ast_nodes, "transform");
120 size_t m = A.size();
121 auto pivot = A[current_row][col];
122
123 for (size_t r = 0; r < m; ++r) {
124 if (r == current_row) {
125 continue;
126 }
127 auto entry = detail::make_expression_ptr(detail::normalize_node(detail::node(A[r][col]), transform_budget));
128 if (entry->is_zero()) {
129 continue;
130 }
131
132 auto factor = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::divide(entry, pivot)), transform_budget));
133
134 for (size_t k = col; k < n; ++k) {
135 auto term = SymbolicExpr::multiply(factor, A[current_row][k]);
136 A[r][k] = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::add(A[r][k], SymbolicExpr::multiply(term, SymbolicExpr::number(-1)))), transform_budget));
137 }
138
139 auto b_term = SymbolicExpr::multiply(factor, b[current_row]);
140 b[r] = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::add(b[r], SymbolicExpr::multiply(b_term, SymbolicExpr::number(-1)))), transform_budget));
141 }
142}
143
144static std::map<std::string, std::shared_ptr<SymbolicExpr>> parametric_linear_solution(
145 const ParametricMatrix& A, const ParametricVector& b,
146 const std::vector<size_t>& pivot_cols, const std::vector<size_t>& pivot_rows,
147 const std::vector<std::string>& unknowns, ComputationContext& context) {
148 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
149 (context).limits().max_ast_nodes, "transform");
150 size_t n = unknowns.size();
151 std::map<std::string, std::shared_ptr<SymbolicExpr>> solution;
152
153 std::set<size_t> pivot_col_set(pivot_cols.begin(), pivot_cols.end());
154 for (size_t col = 0; col < n; ++col) {
155 if (pivot_col_set.find(col) == pivot_col_set.end()) {
156
157 solution[unknowns[col]] = SymbolicExpr::variable(unknowns[col]);
158 }
159 }
160
161 for (size_t k = 0; k < pivot_cols.size(); ++k) {
162 size_t col = pivot_cols[k];
163 size_t row = pivot_rows[k];
164
165 auto value = b[row];
166
167 for (size_t j = col + 1; j < n; ++j) {
168 if (pivot_col_set.find(j) != pivot_col_set.end()) {
169 continue;
170 }
171 auto coeff = detail::make_expression_ptr(detail::normalize_node(detail::node(A[row][j]), transform_budget));
172 if (!parametric_zero(coeff, context)) {
173
174 auto term = SymbolicExpr::multiply(coeff, SymbolicExpr::variable(unknowns[j]));
175 value = SymbolicExpr::add(value, SymbolicExpr::multiply(term, SymbolicExpr::number(-1)));
176 }
177 }
178
179 auto pivot_val = A[row][col];
180 value = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::divide(value, pivot_val)), transform_budget));
181
182 solution[unknowns[col]] = value;
183 }
184 return solution;
185}
186
187std::vector<std::map<std::string, std::shared_ptr<SymbolicExpr>>>
188ParametricSolver::solve_linear_parametric(
189 const std::vector<std::shared_ptr<SymbolicExpr>>& equations,
190 const std::vector<std::string>& unknowns,
191 ComputationContext& context)
192{
193 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
194 (context).limits().max_ast_nodes, "transform");
195 size_t m = equations.size();
196 size_t n = unknowns.size();
197
198 if (m == 0 || n == 0) {
199
200 std::map<std::string, std::shared_ptr<SymbolicExpr>> solution;
201 for (const auto& var : unknowns) {
202 solution[var] = SymbolicExpr::variable(var);
203 }
204 return { solution };
205 }
206
207 std::vector<std::vector<std::shared_ptr<SymbolicExpr>>> A(m, std::vector<std::shared_ptr<SymbolicExpr>>(n));
208 std::vector<std::shared_ptr<SymbolicExpr>> b(m);
209
210 for (size_t i = 0; i < m; ++i) {
211 for (size_t j = 0; j < n; ++j) {
212 A[i][j] = extract_coefficient(equations[i], unknowns[j], context);
213 }
214
215 auto constant = extract_constant_term(equations[i], unknowns, context);
216 b[i] = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::multiply(constant, SymbolicExpr::number(-1))), transform_budget));
217 }
218
219 std::vector<size_t> pivot_cols;
220 std::vector<size_t> pivot_rows;
221 size_t current_row = 0;
222
223 for (size_t col = 0; col < n && current_row < m; ++col) {
224
225 size_t pivot_row = find_parametric_pivot(A, current_row, col, context);
226 if (pivot_row == m) {
227 continue;
228 }
229
230 if (pivot_row != current_row) {
231 std::swap(A[pivot_row], A[current_row]);
232 std::swap(b[pivot_row], b[current_row]);
233 }
234
235 eliminate_parametric_column(A, b, current_row, col, n, context);
236
237 pivot_cols.push_back(col);
238 pivot_rows.push_back(current_row);
239 current_row++;
240 }
241
242 for (size_t r = current_row; r < m; ++r) {
243 auto b_simplified = detail::make_expression_ptr(detail::normalize_node(detail::node(b[r]), transform_budget));
244 if (!parametric_zero(b_simplified, context)) {
245
246 return {};
247 }
248 }
249
250 return {parametric_linear_solution(A, b, pivot_cols, pivot_rows, unknowns, context)};
251}
252
253
255ParametricSolver::solve_polynomial_parametric_checked(
256 const std::vector<std::shared_ptr<SymbolicExpr>>& equations,
257 const std::vector<std::string>& unknowns,
258 const std::vector<std::string>& parameters,
260{
261 constexpr const char* operation = "solve_polynomial_parametric";
262 if (equations.empty() || unknowns.empty()) {
265 "parametric polynomial solve requires equations and unknowns",
266 operation);
267 }
268 auto budget = context.consume_steps(
269 equations.size() * unknowns.size() + 1, operation);
270 if (!budget) {
272 }
273 try {
274 if (is_linear_in_unknowns(equations, unknowns, context))
275 return solve_linear_parametric(equations, unknowns, context);
276 return solve_polynomial_parametric_impl(
277 equations, unknowns, parameters, context);
278 } catch (const CasError& error) {
280 } catch (const std::bad_alloc&) {
283 "allocation failed while solving parametric polynomial system",
284 operation);
285 } catch (const std::exception& ex) {
287 CasErrc::InternalInvariant, ex.what(), operation);
288 }
289}
290
292ParametricSolver::solve_polynomial_parametric_checked(
293 const std::vector<std::shared_ptr<SymbolicExpr>>& equations,
294 const std::vector<std::string>& unknowns,
295 const std::vector<std::string>& parameters)
296{
298 return solve_polynomial_parametric_checked(
299 equations, unknowns, parameters, context);
300}
301
302std::vector<std::map<std::string, std::shared_ptr<SymbolicExpr>>>
303ParametricSolver::solve_system(
304 const std::vector<std::shared_ptr<SymbolicExpr>>& equations,
305 const std::vector<std::string>& unknowns,
306 const std::vector<std::string>& parameters)
307{
308 if (equations.empty() || unknowns.empty()) {
309 return {};
310 }
311
312
314 if (is_linear_in_unknowns(equations, unknowns, context)) {
315 return solve_linear_parametric(equations, unknowns, context);
316 }
317 auto solved = solve_polynomial_parametric_checked(
318 equations, unknowns, parameters, context);
319 if (!solved) throw solved.error();
320 return std::move(solved.value());
321}
322
323
324PiecewiseSolution ParametricSolver::solve_system_piecewise(
325 const std::vector<std::shared_ptr<SymbolicExpr>>& equations,
326 const std::vector<std::string>& unknowns,
327 const std::vector<std::string>& parameters)
328{
329 PiecewiseSolution result;
330 if (equations.empty() || unknowns.empty()) return result;
331 auto solutions = solve_system(equations, unknowns, parameters);
332 if (!solutions.empty()) {
333 PiecewiseSolution::Case only_case;
334 only_case.condition = SymbolicExpr::number(1);
335 only_case.solutions = std::move(solutions);
336 result.cases.push_back(std::move(only_case));
337 }
338 return result;
339}
340
341}
static Result failure(CasErrc code, std::string message, std::string operation={})
SymbolicNodePtr differentiate_node(const SymbolicNodePtr &node, const std::string &variable, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
std::shared_ptr<::LMCAS::SymbolicExpr > make_expression_ptr(SymbolicNodePtr root)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
Result< Tribool > query_nonzero_value(const Node &node, const FactsQuery &facts, Domain domain, ComputationContext &context)
const FactsQuery & no_facts() noexcept
ComputationContext const std::string & operation
SymbolicNodePtr normalize_node(const SymbolicNodePtr &node, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
SymbolicNodePtr substitute_node(const SymbolicNodePtr &node, const std::string &variable, const SymbolicNodePtr &replacement, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
static Tribool parametric_nonzero_value(const std::shared_ptr< SymbolicExpr > &expression, ComputationContext &context)
ExprResult eq(const ExprPtr &lhs, const ExprPtr &rhs, ComputationContext &context)
static size_t find_parametric_pivot(ParametricMatrix &matrix, size_t row, size_t column, ComputationContext &context)
__attribute__((visibility("default"))) ComplexSymbolicResult complex_exp_form_checked(std __attribute__((visibility("default"))) ComplexSymbolicResult complex_exp_form_checked(std __attribute__((visibility("default"))) ComplexSymbolicResult complex_trig_form_checked(std __attribute__((visibility("default"))) ComplexSymbolicResult complex_trig_form_checked(std __attribute__((visibility("default"))) ComplexRootsResult solve_complex_nth_root_checked(std __attribute__((visibility("default"))) ComplexRootsResult solve_complex_nth_root_checked(std __attribute__((visibility("default"))) ComplexRootsResult solve_complex_quadratic_checked(std __attribute__((visibility("default"))) ComplexRootsResult solve_complex_quadratic_checked(std std::shared_ptr< SymbolicExpr > r
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
static std::shared_ptr< SymbolicExpr > extract_coefficient(const std::shared_ptr< SymbolicExpr > &expr, const std::string &var, ComputationContext &context)
std::vector< std::vector< std::shared_ptr< SymbolicExpr > > > ParametricMatrix
@ Complex
Complex numbers (default, least specific)
static bool parametric_residual_zero(const std::shared_ptr< SymbolicExpr > &expression, ComputationContext &context)
Tribool
Tri-state logic value for property queries.
@ True
Property definitely holds
@ False
Property definitely does not hold
Result< ParametricSolutionList > ParametricSolutionsResult
const MultiPoly & b
static std::map< std::string, std::shared_ptr< SymbolicExpr > > parametric_linear_solution(const ParametricMatrix &A, const ParametricVector &b, const std::vector< size_t > &pivot_cols, const std::vector< size_t > &pivot_rows, const std::vector< std::string > &unknowns, ComputationContext &context)
std::vector< std::shared_ptr< SymbolicExpr > > ParametricVector
ResidualCheckResult check_zero_residual(const ExprPtr &residual, ComputationContext &context, const LMCAS::EqvOptions &options)
const std::string const ExprPtr & value
static void eliminate_parametric_column(ParametricMatrix &A, ParametricVector &b, size_t current_row, size_t col, size_t n, ComputationContext &context)
static std::shared_ptr< SymbolicExpr > extract_constant_term(const std::shared_ptr< SymbolicExpr > &expr, const std::vector< std::string > &unknowns, ComputationContext &context)
static bool parametric_zero(const std::shared_ptr< SymbolicExpr > &expression, ComputationContext &context)
含参方程组的分段解,按参数条件分类
含参方程组求解器 ParametricSolver,支持分段解。
单个分段:参数条件及对应的解集
std::shared_ptr< SymbolicExpr > condition
参数满足的条件表达式
内部 AST 定义。