LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
parametric_solver_branches.cpp
浏览该文件的文档.
3#include "solver.hpp"
5#include "poly_utils.hpp"
9#include <algorithm>
10#include <limits>
11
12namespace LMCAS {
13namespace {
14
15using ParametricAssignment = std::map<std::string, std::shared_ptr<SymbolicExpr>>;
16using ParametricBasis = std::vector<std::shared_ptr<SymbolicExpr>>;
17
19 std::optional<detail::AssumptionFacts> assumptions;
20 if (context.assumptions()) { assumptions.emplace(*context.assumptions()); }
21 const FactsQuery& facts = assumptions ? static_cast<const FactsQuery&>(*assumptions) : detail::no_facts();
22 auto nonzero = detail::query_nonzero_value(detail::node(expression), facts, Domain::Complex, context);
23 if (!nonzero) { return Result<Tribool>::failure(nonzero.error()); }
24 if (nonzero.value() == Tribool::True) { return Tribool::False; }
25 if (nonzero.value() == Tribool::False) { return Tribool::True; }
26 auto residual = check_zero_residual(expression, context);
27 if (!residual) { return Result<Tribool>::failure(residual.error()); }
28 return std::holds_alternative<ProvedZeroResidual>(residual.value()) ?
30}
31
32ParametricSolutionList free_unknowns(const std::vector<std::string>& unknowns) {
33 ParametricAssignment solution;
34 for (const auto& variable : unknowns) solution[variable] = SymbolicExpr::variable(variable);
35 return {std::move(solution)};
36}
37
38std::vector<SymbolicExpr> parametric_input_polynomials(
39 const std::vector<std::shared_ptr<SymbolicExpr>>& equations, ComputationContext& context) {
40
41 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
42 (context).limits().max_ast_nodes, "transform");
43 std::vector<SymbolicExpr> input_polys;
44 input_polys.reserve(equations.size());
45 for (const auto& eq : equations) {
46 if (eq && LMCAS::detail::node(eq)) {
47 auto simplified = detail::make_expression_ptr(detail::normalize_node(detail::node(eq), transform_budget));
48 if (simplified && !simplified->is_zero()) {
49 input_polys.push_back(*simplified);
50 }
51 }
52 }
53 return input_polys;
54}
55
56ParametricBasis parametric_basis(const std::vector<SymbolicExpr>& input_polys,
57 const std::vector<std::string>& unknowns, ComputationContext& context) {
58
59 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
60 (context).limits().max_ast_nodes, "transform");
61 auto G_basis = Solver::groebner_basis(input_polys, unknowns);
62
63 std::vector<std::shared_ptr<SymbolicExpr>> basis;
64 basis.reserve(G_basis.size());
65 for (const auto& g : G_basis) {
67 auto simp = detail::make_expression_ptr(detail::normalize_node(detail::node(g_ptr), transform_budget));
68 if (simp && !simp->is_zero()) {
69 basis.push_back(simp);
70 }
71 }
72 return basis;
73}
74
75bool depends_on_remaining_unknown(const SymbolicExpr& expression,
76 const std::vector<std::string>& unknowns,
77 int last) {
78 for (int index = 0; index <= last && index < static_cast<int>(unknowns.size()); ++index) {
79 if (contains(expression, unknowns[index])) {
80 return true;
81 }
82 }
83 return false;
84}
85
86std::shared_ptr<SymbolicExpr> substitute_assignment(
87 const std::shared_ptr<SymbolicExpr>& expression, const ParametricAssignment& assignment, ComputationContext& context) {
88
89 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
90 (context).limits().max_ast_nodes, "transform");
91 auto result = expression;
92 for (const auto& [name, value] : assignment) {
94 if (!result) {
95 return nullptr;
96 }
97 }
98 return detail::make_expression_ptr(detail::normalize_node(detail::node(result), transform_budget));
99}
100
103 const std::vector<std::string>& unknowns_;
104 const std::vector<std::string>& parameters_;
106
107 Result<bool> reduce_basis(int position, const ParametricAssignment& partial,
108 ParametricBasis& reduced) const {
109 reduced.reserve(basis_.size());
110 for (const auto& polynomial : basis_) {
111 auto expression = substitute_assignment(polynomial, partial, context_);
112 if (!expression) {
114 "assignment substitution failed", "solve.parametric");
115 }
116 if (expression->is_zero()) {
117 continue;
118 }
119 if (!depends_on_remaining_unknown(*expression, unknowns_, position)) {
120 auto residual = parameter_constraint(expression, context_);
121 if (!residual) return Result<bool>::failure(residual.error());
122 if (residual.value() == Tribool::False) return false;
123 if (residual.value() != Tribool::True)
125 "remaining parameter constraint is not proved", "solve.parametric");
126 continue;
127 }
128 reduced.push_back(expression);
129 }
130 return true;
131 }
132
134 int position) const {
135 const auto& variable = unknowns_[position];
136 std::shared_ptr<SymbolicExpr> target;
137 ExprPtr leading;
138 int best_degree = std::numeric_limits<int>::max();
139 bool appears = false;
140 for (const auto& expression : reduced) {
141 if (!contains(*expression, variable)) {
142 continue;
143 }
144 appears = true;
145 if (depends_on_remaining_unknown(*expression, unknowns_, position - 1)) {
146 continue;
147 }
148 auto polynomial = symbolic_to_poly<SymbolicPolyCoeff>(expression, variable);
149 if (!polynomial) {
150 if (polynomial.error().code != CasErrc::UnsupportedExpression) {
151 return Result<std::shared_ptr<SymbolicExpr>>::failure(polynomial.error());
152 }
153 continue;
154 }
155 int degree = polynomial.value().degree();
156 if (degree >= 1 && degree < best_degree) {
157 best_degree = degree;
158 target = expression;
159 leading = polynomial.value().coeffs.back().val;
160 }
161 }
162 if (!target && appears) {
164 "No convertible polynomial target for remaining variable", "solve.parametric");
165 }
166 if (target) {
167 for (const auto& parameter : parameters_) {
168 if (!contains(*leading, parameter)) continue;
169 auto zero = parameter_constraint(leading, context_);
170 if (!zero) return Result<std::shared_ptr<SymbolicExpr>>::failure(zero.error());
171 if (zero.value() != Tribool::False)
173 "leading parameter coefficient is not proved nonzero", "solve.parametric");
174 break;
175 }
176 }
177 return target;
178 }
179
181 const ParametricAssignment& partial) const {
182 auto next = partial;
183 const auto& variable = unknowns_[position];
184 next[variable] = SymbolicExpr::variable(variable);
185 return solve(position - 1, next);
186 }
187
188public:
190 const std::vector<std::string>& unknowns,
191 const std::vector<std::string>& parameters,
193 : basis_(basis), unknowns_(unknowns), parameters_(parameters), context_(context) {}
194
195 ParametricSolutionsResult solve(int position, const ParametricAssignment& partial) const {
196 ParametricBasis reduced;
197 auto consistent = reduce_basis(position, partial, reduced);
198 if (!consistent) return ParametricSolutionsResult::failure(consistent.error());
199 if (!consistent.value()) {
200 return ParametricSolutionList{};
201 }
202 if (position < 0) {
203 return ParametricSolutionList{partial};
204 }
205 auto selected = select_target(reduced, position);
206 if (!selected) return ParametricSolutionsResult::failure(selected.error());
207 if (!selected.value()) {
208 return solve_free_unknown(position, partial);
209 }
210 const auto& variable = unknowns_[position];
211 auto solved = solve_finite_checked(selected.value(), variable, context_);
212 if (!solved) {
213 return ParametricSolutionsResult::failure(solved.error());
214 }
215 auto roots = std::move(solved.value());
216 if (roots.empty()) {
217 return ParametricSolutionList{};
218 }
220 for (const auto& root : roots) {
221 auto next = partial;
222 next[variable] = root;
223 auto branch = solve(position - 1, next);
224 if (!branch) {
225 return branch;
226 }
227 auto& values = branch.value();
228 results.insert(results.end(), std::make_move_iterator(values.begin()),
229 std::make_move_iterator(values.end()));
230 }
231 return results;
232 }
233};
234
235}
236
237ParametricSolutionsResult ParametricSolver::solve_polynomial_parametric_impl(
238 const std::vector<std::shared_ptr<SymbolicExpr>>& equations,
239 const std::vector<std::string>& unknowns,
240 const std::vector<std::string>& parameters,
241 ComputationContext& context)
242{
243 if (equations.empty() || unknowns.empty()) {
244 return free_unknowns(unknowns);
245 }
246 auto input_polys = parametric_input_polynomials(equations, context);
247 if (input_polys.empty()) {
248 return free_unknowns(unknowns);
249 }
251 if (unknowns.size() == 1) {
252 basis.reserve(input_polys.size());
253 for (const auto& polynomial : input_polys)
254 basis.push_back(detail::make_expression_ptr(polynomial));
255 } else {
256 basis = parametric_basis(input_polys, unknowns, context);
257 }
258 for (const auto& polynomial : basis) {
259 if (!depends_on_remaining_unknown(*polynomial, unknowns, static_cast<int>(unknowns.size()) - 1)) {
260 auto residual = parameter_constraint(polynomial, context);
261 if (!residual) return ParametricSolutionsResult::failure(residual.error());
262 if (residual.value() == Tribool::False)
263 return ParametricSolutionList{};
264 if (residual.value() != Tribool::True)
266 "parameter-only constraint is not proved", "solve.parametric");
267 }
268 }
269 ParametricBranchSolver branches(basis, unknowns, parameters, context);
271 return branches.solve(static_cast<int>(unknowns.size()) - 1, empty);
272}
273
274}
static Result failure(CasErrc code, std::string message, std::string operation={})
Result< std::shared_ptr< SymbolicExpr > > select_target(const ParametricBasis &reduced, int position) const
ParametricSolutionsResult solve(int position, const ParametricAssignment &partial) const
Result< bool > reduce_basis(int position, const ParametricAssignment &partial, ParametricBasis &reduced) const
ParametricSolutionsResult solve_free_unknown(int position, const ParametricAssignment &partial) const
ParametricBranchSolver(const ParametricBasis &basis, const std::vector< std::string > &unknowns, const std::vector< std::string > &parameters, ComputationContext &context)
Result< Tribool > parameter_constraint(const ExprPtr &expression, ComputationContext &context)
bool depends_on_remaining_unknown(const SymbolicExpr &expression, const std::vector< std::string > &unknowns, int last)
ParametricSolutionList free_unknowns(const std::vector< std::string > &unknowns)
std::shared_ptr< SymbolicExpr > substitute_assignment(const std::shared_ptr< SymbolicExpr > &expression, const ParametricAssignment &assignment, ComputationContext &context)
ParametricBasis parametric_basis(const std::vector< SymbolicExpr > &input_polys, const std::vector< std::string > &unknowns, ComputationContext &context)
std::vector< SymbolicExpr > parametric_input_polynomials(const std::vector< std::shared_ptr< SymbolicExpr > > &equations, ComputationContext &context)
std::map< std::string, std::shared_ptr< SymbolicExpr > > ParametricAssignment
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
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)
const std::string & variable
ExprResult eq(const ExprPtr &lhs, const ExprPtr &rhs, ComputationContext &context)
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
@ Complex
Complex numbers (default, least specific)
@ Unknown
Property cannot be determined
@ True
Property definitely holds
@ False
Property definitely does not hold
Result< ParametricSolutionList > ParametricSolutionsResult
bool contains(const SymbolicExpr &expression, const std::string &variable)
LMCAS::FiniteSolveResult solve_finite_checked(const std::shared_ptr< SymbolicExpr > &expr, const std::string &var, ComputationContext &context, const SolveOptions &opts)
std::shared_ptr< SymbolicExpr > ExprPtr
const ExprPtr & target
const std::vector< FGLMPoly > & basis
ExprSetResult roots(const ExprPtr &expression, const std::string &variable, ComputationContext &context, const SolveOptions &options)
ResidualCheckResult check_zero_residual(const ExprPtr &residual, ComputationContext &context, const LMCAS::EqvOptions &options)
const std::string const ExprPtr & value
std::vector< std::map< std::string, std::shared_ptr< SymbolicExpr > > > ParametricSolutionList
__attribute__((visibility("default"))) Result< EqvProfile > eqv_profile_from_name(const std const std::string & name
含参方程组求解器 ParametricSolver,支持分段解。
多项式转换与精确符号多项式代数。
方程求解策略调度器 solve_dispatch 及相关选项结构体.
多项式方程组求解器 Solver,包含 Gröbner 基、线性系统求解。
内部 AST 定义。