LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
solver_groebner_system.cpp
浏览该文件的文档.
1#include <stdexcept>
3#include "solver.hpp"
5#include "fglm.hpp"
7#include "poly_utils.hpp"
12#include <iterator>
13#include <limits>
14#include <optional>
15
16namespace LMCAS {
17namespace {
18using namespace solver_detail;
19using PolynomialSolution = std::map<std::string, SymbolicExpr>;
20using PolynomialSolutions = std::vector<PolynomialSolution>;
21using Expressions = std::vector<std::shared_ptr<SymbolicExpr>>;
22
23std::shared_ptr<SymbolicExpr> clear_denominators(
24 const SymbolicExpr& equation, Expressions& constraints, ComputationContext& context) {
25
26 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
27 (context).limits().max_ast_nodes, "transform");
28 if (!detail::node(equation)) return nullptr;
29 std::vector<std::shared_ptr<const SymbolicNode>> factors;
30 Expressions local_constraints;
31 if (!collect_denominator_factors(detail::node(equation), factors, local_constraints)) {
32 return nullptr;
33 }
34 auto cleared = to_ptr(equation);
35 if (factors.empty()) {
36 cleared = detail::make_expression_ptr(detail::normalize_node(detail::node(cleared), transform_budget));
37 } else if (auto sum = std::dynamic_pointer_cast<const AddNode>(detail::node(cleared))) {
38 std::vector<std::shared_ptr<const SymbolicNode>> terms;
39 terms.reserve(sum->operands().size());
40 for (const auto& term : sum->operands()) {
41 auto product = multiply_no_expand(term, factors);
42 terms.push_back(detail::node(product));
43 }
45 } else {
46 cleared = multiply_no_expand(detail::node(cleared), factors);
47 }
48 if (!cleared || !detail::node(cleared) || !is_polynomial_node(detail::node(cleared))) {
49 return nullptr;
50 }
51 for (const auto& constraint : local_constraints) constraints.push_back(constraint);
52 return cleared;
53}
54
55std::shared_ptr<SymbolicExpr> substitute_solution(
56 const std::shared_ptr<SymbolicExpr>& expression, const PolynomialSolution& solution, ComputationContext& context) {
57
58 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
59 (context).limits().max_ast_nodes, "transform");
60 auto result = expression;
61 for (const auto& [name, value] : solution) {
63 if (!result) return nullptr;
64 }
65 return detail::make_expression_ptr(detail::normalize_node(detail::node(result), transform_budget));
66}
67
69 const Expressions& constraints, ComputationContext& context) {
70 std::optional<detail::AssumptionFacts> adapter;
71 if (context.assumptions()) adapter.emplace(*context.assumptions());
72 const FactsQuery& facts = adapter ? static_cast<const FactsQuery&>(*adapter) : detail::no_facts();
73 PolynomialSolutions filtered;
74 filtered.reserve(candidates.size());
75 for (auto& solution : candidates) {
76 bool valid = true;
77 for (const auto& denominator : constraints) {
78 auto bound = substitute_solution(denominator, solution, context);
80 "denominator substitution failed", "solve_polynomial_system");
82 if (!nonzero) return PolynomialSystemResult::failure(nonzero.error());
83 if (nonzero.value() == Tribool::False) { valid = false; break; }
84 if (nonzero.value() != Tribool::True)
86 "candidate denominator is not proved nonzero", "solve_polynomial_system");
87 }
88 if (valid) filtered.push_back(std::move(solution));
89 }
90 return filtered;
91}
92
93PolynomialSystemResult solve_single_equation(const SymbolicExpr& equation,
94 const std::string& variable,
98 if (!solved) return PolynomialSystemResult::failure(solved.error());
99 PolynomialSolutions solutions;
100 for (const auto& root : solved.value()) solutions.push_back({{variable, *root}});
101 return solutions;
102}
103
104Result<void> validate_basis_input(const std::vector<SymbolicExpr>& equations,
105 const std::vector<std::string>& variables,
108 constexpr const char* operation = "solve_polynomial_system";
109 for (const auto& equation : equations) {
110 auto budget = context.consume_steps(1, operation);
111 if (!budget) return Result<void>::failure(budget.error());
112 groebner_detail::PolyBuilder builder(variables, conversion, true);
113 detail::node(equation)->accept(builder);
114 if (builder.failed) return Result<void>::failure(
115 CasErrc::Inconclusive, "equation is not an exact polynomial in the system variables", operation);
116 }
117 return Result<void>::success();
118}
119
121 const SymbolicExpr& polynomial, const std::vector<std::string>& variables,
123 std::shared_ptr<SymbolicExpr>& simplified) try {
124 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
125 (context).limits().max_ast_nodes, "transform");
126 constexpr const char* operation = "solve_polynomial_system";
127 auto budget = context.consume_steps(1, operation);
128 if (!budget) return Result<groebner_detail::Poly>::failure(budget.error());
129 simplified = detail::make_expression_ptr(detail::normalize_node(detail::node(polynomial), transform_budget));
130 if (!simplified) return Result<groebner_detail::Poly>::failure(
131 CasErrc::Inconclusive, "basis polynomial cannot be converted", operation);
132 groebner_detail::PolyBuilder builder(variables, conversion, true);
133 detail::node(simplified)->accept(builder);
135 CasErrc::Inconclusive, "basis polynomial cannot be converted", operation);
136 return builder.get_result();
137} catch (const CasError& error) {
139} catch (const std::bad_alloc&) {
140 return Result<groebner_detail::Poly>::failure(CasErrc::ResourceLimit, "allocation failed", "solve_polynomial_system");
141} catch (const std::length_error& error) {
142 return Result<groebner_detail::Poly>::failure(CasErrc::ResourceLimit, error.what(), "solve_polynomial_system");
143} catch (const std::exception& error) {
144 return Result<groebner_detail::Poly>::failure(CasErrc::InternalInvariant, error.what(), "solve_polynomial_system");
145}
146
147Result<Expressions> simplified_basis(const std::vector<SymbolicExpr>& equations,
148 const std::vector<std::string>& variables,
150 constexpr const char* operation = "solve_polynomial_system";
151 groebner_detail::PolyContext conversion(variables, context);
152 auto validated = validate_basis_input(equations, variables, conversion, context);
153 if (!validated) return Result<Expressions>::failure(validated.error());
154 auto groebner = groebner_detail::reduced_groebner_basis_impl(equations, variables, context);
156 std::vector<FGLMPoly> leading;
157 basis.reserve(groebner.size());
158 leading.reserve(groebner.size());
159 for (const auto& polynomial : groebner) {
160 std::shared_ptr<SymbolicExpr> simplified;
161 auto converted = convert_basis_polynomial(polynomial, variables, conversion, context, simplified);
162 if (!converted) return Result<Expressions>::failure(converted.error());
163 if (simplified->is_number() && !simplified->is_zero()) return Expressions{simplified};
164 if (converted.value().is_zero()) continue;
165 FGLMPoly term(variables.size());
166 term.add_term(converted.value().lead_monomial(), converted.value().lead_coeff());
167 leading.push_back(std::move(term));
168 basis.push_back(std::move(simplified));
169 }
170 if (!is_zero_dimensional(leading, variables.size())) {
172 "polynomial system has no certified finite solution set", operation);
173 }
174 return basis;
175}
177public:
179 const std::vector<std::string>& variables,
181 : basis_(basis), variables_(variables), context_(context) {}
182
183 PolynomialSystemResult solve(int variable, const PolynomialSolution& partial) const;
184
185private:
187 const std::vector<std::string>& variables_;
189
190 bool depends_on_remaining(const SymbolicExpr& expression, int last) const;
192 const PolynomialSolution& partial) const;
194 int variable, bool& appears) const;
195};
196
197bool PolynomialSystemSearch::depends_on_remaining(const SymbolicExpr& expression,
198 int last) const {
199 for (int index = 0; index <= last && index < static_cast<int>(variables_.size()); ++index) {
200 if (contains(expression, variables_[index])) return true;
201 }
202 return false;
203}
204
206 int variable, const PolynomialSolution& partial) const {
207 Expressions reduced;
208 reduced.reserve(basis_.size());
209 for (const auto& polynomial : basis_) {
210 auto substituted = substitute_solution(polynomial, partial, context_);
211 if (!substituted) { return Result<std::optional<Expressions>>::failure(
212 CasErrc::InternalInvariant, "basis substitution failed", "solve_polynomial_system"); }
213 if (substituted->is_zero()) { continue; }
214 if (!depends_on_remaining(*substituted, variable)) {
215 auto proof = check_zero_residual(substituted, context_);
216 if (!proof) { return Result<std::optional<Expressions>>::failure(proof.error()); }
217 if (std::holds_alternative<ProvedZeroResidual>(proof.value())) { continue; }
218 std::optional<detail::AssumptionFacts> adapter;
219 if (context_.assumptions()) { adapter.emplace(*context_.assumptions()); }
220 const FactsQuery& facts = adapter ? static_cast<const FactsQuery&>(*adapter) : detail::no_facts();
221 auto nonzero = detail::query_nonzero_value(detail::node(substituted), facts, Domain::Complex, context_);
222 if (!nonzero) { return Result<std::optional<Expressions>>::failure(nonzero.error()); }
223 if (nonzero.value() == Tribool::True) { return std::optional<Expressions>{}; }
225 "remaining system constraint is not proved", "solve_polynomial_system");
226 }
227 reduced.push_back(substituted);
228 }
229 return std::optional<Expressions>{std::move(reduced)};
230}
231
233 const Expressions& reduced, int variable, bool& appears) const {
234 std::shared_ptr<SymbolicExpr> target;
235 int best_degree = std::numeric_limits<int>::max();
236 const auto& name = variables_[variable];
237 for (const auto& polynomial : reduced) {
238 if (!contains(*polynomial, name)) continue;
239 appears = true;
240 if (depends_on_remaining(*polynomial, variable - 1)) continue;
241 auto univariate = symbolic_to_poly<SymbolicPolyCoeff>(polynomial, name);
242 if (!univariate) {
243 if (univariate.error().code != CasErrc::UnsupportedExpression) {
244 return Result<std::shared_ptr<SymbolicExpr>>::failure(univariate.error());
245 }
246 continue;
247 }
248 int degree = univariate.value().degree();
249 if (degree >= 1 && degree < best_degree) {
250 best_degree = degree;
251 target = polynomial;
252 }
253 }
254 return target;
255}
256
258 int variable, const PolynomialSolution& partial) const {
259 auto reduced = substitute_basis(variable, partial);
260 if (!reduced) return PolynomialSystemResult::failure(reduced.error());
261 if (!reduced.value()) return PolynomialSolutions{};
262 if (variable < 0) return PolynomialSolutions{partial};
263 const auto& name = variables_[variable];
264 bool appears = false;
265 auto target = select_equation(*reduced.value(), variable, appears);
266 if (!target) return PolynomialSystemResult::failure(target.error());
267 if (!appears) {
269 "remaining variable is not constrained", "solve_polynomial_system");
270 }
271 if (!target.value()) {
273 "No convertible polynomial target for remaining variable", "solve_polynomial_system");
274 }
275 auto solved = solve_finite_checked(target.value(), name, context_, SolveOptions{});
276 if (!solved) return PolynomialSystemResult::failure(solved.error());
277 PolynomialSolutions results;
278 for (const auto& root : solved.value()) {
279 auto next = partial;
280 next.insert_or_assign(name, *root);
281 auto solved_branch = solve(variable - 1, next);
282 if (!solved_branch) return solved_branch;
283 auto& values = solved_branch.value();
284 results.insert(results.end(), std::make_move_iterator(values.begin()),
285 std::make_move_iterator(values.end()));
286 }
287 return results;
288}
289
291 const std::vector<SymbolicExpr>& equations,
292 const std::vector<std::string>& variables, ComputationContext& context) {
293 std::vector<SymbolicExpr> cleared_equations;
294 Expressions constraints;
295 cleared_equations.reserve(equations.size());
296 for (const auto& equation : equations) {
297 auto cleared = clear_denominators(equation, constraints, context);
299 "equation cannot be converted to a polynomial without losing its domain", "solve_polynomial_system");
300 cleared_equations.push_back(*cleared);
301 }
302 if (cleared_equations.size() == 1 && variables.size() == 1) {
303 auto solved = solve_single_equation(cleared_equations[0], variables[0], context);
304 if (!solved) return solved;
305 return filter_denominators(std::move(solved.value()), constraints, context);
306 }
307 auto basis = simplified_basis(cleared_equations, variables, context);
308 if (!basis) return PolynomialSystemResult::failure(basis.error());
309 if (basis.value().size() == 1 && basis.value().front()->is_number() &&
310 !basis.value().front()->is_zero()) return PolynomialSolutions{};
311 PolynomialSystemSearch search(basis.value(), variables, context);
312 auto solved = search.solve(static_cast<int>(variables.size()) - 1, PolynomialSolution{});
313 if (!solved) return solved;
314 return filter_denominators(std::move(solved.value()), constraints, context);
315}
316}
317
318PolynomialSystemResult Solver::solve_polynomial_system_checked(
319 const std::vector<SymbolicExpr>& equations,
320 const std::vector<std::string>& variables, ComputationContext& context) {
321 constexpr const char* operation = "solve_polynomial_system";
322 if (equations.empty() || variables.empty()) {
325 "polynomial system requires equations and variables", operation);
326 }
327 auto budget = context.consume_steps(equations.size() * variables.size() + 1, operation);
328 if (!budget) return PolynomialSystemResult::failure(budget.error());
329 try {
330 return solve_polynomial_system_impl(equations, variables, context);
331 } catch (const CasError& error) {
333 } catch (const std::bad_alloc&) {
336 "allocation failed while solving polynomial system", operation);
337 } catch (const std::length_error& error) {
338 return PolynomialSystemResult::failure(CasErrc::ResourceLimit, error.what(), operation);
339 } catch (const std::exception& error) {
340 return PolynomialSystemResult::failure(CasErrc::InternalInvariant, error.what(), operation);
341 }
342}
343
344PolynomialSystemResult Solver::solve_polynomial_system_checked(
345 const std::vector<SymbolicExpr>& equations,
346 const std::vector<std::string>& variables) {
348 return solve_polynomial_system_checked(equations, variables, context);
349}
350}
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(T value)
virtual void accept(LMCAS::detail::SymbolicVisitor &visitor) const =0
PolynomialSystemResult solve(int variable, const PolynomialSolution &partial) const
PolynomialSystemSearch(const Expressions &basis, const std::vector< std::string > &variables, ComputationContext &context)
Result< std::shared_ptr< SymbolicExpr > > select_equation(const Expressions &reduced, int variable, bool &appears) const
Result< std::optional< Expressions > > substitute_basis(int variable, const PolynomialSolution &partial) const
FGLM 算法:零维理想 Gröbner 基在不同单项式序之间的转换.
PolynomialSystemResult solve_polynomial_system_impl(const std::vector< SymbolicExpr > &equations, const std::vector< std::string > &variables, ComputationContext &context)
std::vector< std::shared_ptr< SymbolicExpr > > Expressions
PolynomialSystemResult filter_denominators(PolynomialSolutions candidates, const Expressions &constraints, ComputationContext &context)
Result< Expressions > simplified_basis(const std::vector< SymbolicExpr > &equations, const std::vector< std::string > &variables, ComputationContext &context)
Result< groebner_detail::Poly > convert_basis_polynomial(const SymbolicExpr &polynomial, const std::vector< std::string > &variables, groebner_detail::PolyContext &conversion, ComputationContext &context, std::shared_ptr< SymbolicExpr > &simplified)
std::shared_ptr< SymbolicExpr > clear_denominators(const SymbolicExpr &equation, Expressions &constraints, ComputationContext &context)
Result< void > validate_basis_input(const std::vector< SymbolicExpr > &equations, const std::vector< std::string > &variables, groebner_detail::PolyContext &conversion, ComputationContext &context)
PolynomialSystemResult solve_single_equation(const SymbolicExpr &equation, const std::string &variable, ComputationContext &context)
std::shared_ptr< SymbolicExpr > substitute_solution(const std::shared_ptr< SymbolicExpr > &expression, const PolynomialSolution &solution, ComputationContext &context)
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
std::shared_ptr< const Node > make_node(Args &&... args)
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)
std::vector< SymbolicExpr > reduced_groebner_basis_impl(const std::vector< SymbolicExpr > &polynomials, const std::vector< std::string > &variables, ComputationContext &context)
bool is_polynomial_node(const std::shared_ptr< const SymbolicNode > &node)
std::shared_ptr< SymbolicExpr > multiply_no_expand(const std::shared_ptr< const SymbolicNode > &term, const std::vector< std::shared_ptr< const SymbolicNode > > &denominator_factors)
bool collect_denominator_factors(const std::shared_ptr< const SymbolicNode > &node, std::vector< std::shared_ptr< const SymbolicNode > > &denominator_factors, std::vector< std::shared_ptr< SymbolicExpr > > &denominator_constraints)
std::shared_ptr< SymbolicExpr > to_ptr(const SymbolicExpr &expression)
const std::string & variable
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
@ Complex
Complex numbers (default, least specific)
@ True
Property definitely holds
@ False
Property definitely does not hold
bool is_zero_dimensional(const std::vector< FGLMPoly > &basis, size_t num_vars)
bool contains(const SymbolicExpr &expression, const std::string &variable)
Result< std::vector< std::map< std::string, SymbolicExpr > > > PolynomialSystemResult
多项式方程组求解器,提供线性系统求解、Gröbner 基计算及多项式系统求解功能。
LMCAS::FiniteSolveResult solve_finite_checked(const std::shared_ptr< SymbolicExpr > &expr, const std::string &var, ComputationContext &context, const SolveOptions &opts)
const ExprPtr & target
const std::vector< FGLMPoly > & basis
ResidualCheckResult check_zero_residual(const ExprPtr &residual, ComputationContext &context, const LMCAS::EqvOptions &options)
const std::string const ExprPtr & value
const ExprPtr & denominator
__attribute__((visibility("default"))) Result< EqvProfile > eqv_profile_from_name(const std const std::string & name
ExprSetResult solve(const ExprPtr &equation, const std::string &variable, ComputationContext &context, const SolveOptions &options)
方程求解选项,控制数值求解行为和结果形式.
多项式转换与精确符号多项式代数。
方程求解策略调度器 solve_dispatch 及相关选项结构体.
多项式方程组求解器 Solver,包含 Gröbner 基、线性系统求解。
内部 AST 定义。