LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
inequality_solver_polynomial.cpp
浏览该文件的文档.
3#include "poly_utils.hpp"
6#include "newton_raphson.hpp"
7#include "root_of_utils.hpp"
12#include <algorithm>
13#include <cmath>
14#include <limits>
15#include <memory>
16#include <optional>
17#include <utility>
18#include <vector>
19
20namespace LMCAS {
21namespace {
22
23constexpr const char* kCheckedInequalityOperation = "solve_inequality_checked";
24
25void check_interval_forest(const std::vector<Interval>& intervals,
26 detail::RewriteBudget& budget) {
27 std::size_t nodes = 0;
28 for (const auto& interval : intervals) {
29 nodes = budget.append_size(nodes, budget.measure(detail::node(interval.lower.value)));
30 nodes = budget.append_size(nodes, budget.measure(detail::node(interval.upper.value)));
31 }
32}
33
34bool rational_is_negative(const Rational& value) {
35 return value.get_numerator().is_negative();
36}
37
38bool constant_satisfies(const Rational& value, InequalityType type) {
39 const bool zero = value == Rational(0);
40 const bool negative = rational_is_negative(value);
41 switch (type) {
42 case InequalityType::GreaterThan: return !negative && !zero;
43 case InequalityType::GreaterEqual: return !negative;
44 case InequalityType::LessThan: return negative;
45 case InequalityType::LessEqual: return negative || zero;
46 }
47 return false;
48}
49
50Endpoint finite_endpoint(const std::shared_ptr<SymbolicExpr>& value, bool strict) {
51 return strict ? Endpoint::open(value) : Endpoint::closed(value);
52}
53
55 const Polynomial<Rational>& polynomial,
56 const std::shared_ptr<SymbolicExpr>& boundary,
57 const Rational& rational_part,
58 const Rational& radical_coefficient,
59 const Rational& radicand,
61 auto steps = context.consume_steps(2, kCheckedInequalityOperation);
62 if (!steps) {
63 return steps;
64 }
65
66 if (!boundary || !LMCAS::detail::node(boundary) || polynomial.coeffs.size() < 3) {
69 "quadratic boundary verification could not construct an expression",
71 }
72
73 const Rational& constant = polynomial.coeffs[0];
74 const Rational& linear = polynomial.coeffs[1];
75 const Rational& quadratic = polynomial.coeffs[2];
76 const Rational residual_rational =
77 quadratic * (rational_part * rational_part +
78 radical_coefficient * radical_coefficient * radicand) +
79 linear * rational_part + constant;
80 const Rational residual_radical =
81 Rational(2) * quadratic * rational_part * radical_coefficient +
82 linear * radical_coefficient;
83 if (residual_rational != Rational(0) ||
84 residual_radical != Rational(0)) {
87 "quadratic boundary could not be verified by exact substitution",
89 }
90 return Result<void>::success();
91}
92
93}
94
96 const Polynomial<Rational>& polynomial, const Rational& rational_part,
97 bool wants_positive, bool leading_positive, bool strict,
98 detail::RewriteBudget& budget) {
99 auto& context = budget.context();
100 auto root = SymbolicExpr::number(rational_part);
101 auto verified = verify_quadratic_boundary(
102 polynomial, root, rational_part, Rational(0), Rational(0), context);
103 if (!verified) {
104 return Result<std::vector<Interval>>::failure(verified.error());
105 }
106
107 if (wants_positive == leading_positive) {
108 if (!strict) {
109 return Result<std::vector<Interval>>::success({Interval::entire_line()});
110 }
111 std::vector<Interval> rays{
112 Interval{Endpoint::neg_inf(), Endpoint::open(root)},
113 Interval{Endpoint::open(root), Endpoint::pos_inf()}
114 };
115 check_interval_forest(rays, budget);
116 return Result<std::vector<Interval>>::success(std::move(rays));
117 }
118 if (strict) {
119 return Result<std::vector<Interval>>::success({});
120 }
121 std::vector<Interval> point{Interval::point(root)};
122 check_interval_forest(point, budget);
123 return Result<std::vector<Interval>>::success(std::move(point));
124}
125
127 std::shared_ptr<SymbolicExpr> first;
128 std::shared_ptr<SymbolicExpr> second;
129};
130
132 const Polynomial<Rational>& polynomial, const Rational& rational_part,
133 const Rational& denominator, const Rational& discriminant,
134 detail::RewriteBudget& budget) {
135 auto& context = budget.context();
137 SymbolicExpr::sqrt(SymbolicExpr::number(discriminant))), budget));
138 const Rational first_radical_coefficient = Rational(-1) / denominator;
139 const Rational second_radical_coefficient = Rational(1) / denominator;
141 SymbolicExpr::add(SymbolicExpr::number(rational_part),
142 SymbolicExpr::multiply(SymbolicExpr::number(first_radical_coefficient),
143 sqrt_discriminant))), budget));
145 SymbolicExpr::add(SymbolicExpr::number(rational_part),
146 SymbolicExpr::multiply(SymbolicExpr::number(second_radical_coefficient),
147 sqrt_discriminant))), budget));
148 budget.append_size(budget.measure(detail::node(first_formula)),
149 budget.measure(detail::node(second_formula)));
150 auto first_verified = verify_quadratic_boundary(
151 polynomial, first_formula, rational_part,
152 first_radical_coefficient, discriminant, context);
153 if (!first_verified) {
154 return Result<QuadraticBoundaries>::failure(first_verified.error());
155 }
156 auto second_verified = verify_quadratic_boundary(
157 polynomial, second_formula, rational_part,
158 second_radical_coefficient, discriminant, context);
159 if (!second_verified) {
160 return Result<QuadraticBoundaries>::failure(second_verified.error());
161 }
163 {std::move(first_formula), std::move(second_formula)});
164}
165
166Result<IntervalUnion> InequalitySolver::solve_exact_quadratic_inequality(
167 const Polynomial<Rational>& polynomial,
168 InequalityType type,
169 ComputationContext& context) {
170 detail::RewriteBudget budget(context, context.limits().max_recursion_depth,
171 context.limits().max_ast_nodes, kCheckedInequalityOperation);
172 const Rational& constant = polynomial.coeffs[0];
173 const Rational& linear = polynomial.coeffs[1];
174 const Rational& quadratic = polynomial.coeffs[2];
175 if (quadratic == Rational(0)) {
178 "quadratic polynomial has a zero leading coefficient",
179 kCheckedInequalityOperation);
180 }
181
182 auto steps = context.consume_steps(3, kCheckedInequalityOperation);
183 if (!steps) {
185 }
186 const Rational discriminant = linear * linear - Rational(4) * quadratic * constant;
187 const bool leading_positive = !rational_is_negative(quadratic);
188 const bool wants_positive = type == InequalityType::GreaterThan ||
190 const bool strict = type == InequalityType::GreaterThan ||
192
193 if (rational_is_negative(discriminant)) {
194 const bool satisfied = wants_positive == leading_positive;
196 satisfied ? IntervalUnion::entire_line() : IntervalUnion::empty());
197 }
198
199 const Rational denominator = Rational(2) * quadratic;
200 const Rational neg_linear = Rational(0) - linear;
201 const Rational rational_part = neg_linear / denominator;
202 if (discriminant == Rational(0)) {
203 auto intervals = double_root_inequality_intervals(polynomial, rational_part,
204 wants_positive, leading_positive, strict, budget);
205 if (!intervals) {
206 return Result<IntervalUnion>::failure(intervals.error());
207 }
209 IntervalUnion::from_checked_normalized(std::move(intervals.value())));
210 }
211
212 auto boundaries = verified_quadratic_boundaries(
213 polynomial, rational_part, denominator, discriminant, budget);
214 if (!boundaries) {
215 return Result<IntervalUnion>::failure(boundaries.error());
216 }
217 auto lower = leading_positive ? boundaries.value().first : boundaries.value().second;
218 auto upper = leading_positive ? boundaries.value().second : boundaries.value().first;
219
220 const bool outside = wants_positive == leading_positive;
221 if (outside) {
222 std::vector<Interval> rays{
223 Interval{Endpoint::neg_inf(), finite_endpoint(lower, strict)},
224 Interval{finite_endpoint(upper, strict), Endpoint::pos_inf()}
225 };
226 check_interval_forest(rays, budget);
228 IntervalUnion::from_checked_normalized(std::move(rays)));
229 }
230 std::vector<Interval> interval{
231 Interval{finite_endpoint(lower, strict), finite_endpoint(upper, strict)}
232 };
235 IntervalUnion::from_checked_normalized(std::move(interval)));
236}
237namespace {
238
240 const Polynomial<Rational>& polynomial,
241 InequalityType type,
243 auto step = context.consume_steps(1, kCheckedInequalityOperation);
244 if (!step) {
245 return Result<IntervalUnion>::failure(step.error());
246 }
247
248 if (polynomial.degree() <= 0) {
249 const Rational value = polynomial.is_zero()
250 ? Rational(0)
251 : polynomial.coeffs[0];
254 ? IntervalUnion::entire_line()
255 : IntervalUnion::empty());
256 }
257
258 if (polynomial.degree() != 1 || polynomial.coeffs.size() < 2) {
261 "checked inequality solving currently supports exact polynomials through degree two",
263 }
264
265 const Rational& intercept = polynomial.coeffs[0];
266 const Rational& slope = polynomial.coeffs[1];
267 if (slope == Rational(0)) {
270 "affine polynomial has a zero leading coefficient",
272 }
273
274 const Rational boundary = (Rational(0) - intercept) / slope;
275 auto boundary_expr = SymbolicExpr::number(boundary);
276 auto nodes = context.require_ast_nodes(1, kCheckedInequalityOperation);
277 if (!nodes) {
278 return Result<IntervalUnion>::failure(nodes.error());
279 }
280
281 const bool wants_greater = type == InequalityType::GreaterThan ||
283 const bool strict = type == InequalityType::GreaterThan ||
285 const bool solution_above = wants_greater != rational_is_negative(slope);
286 const Endpoint finite = strict
287 ? Endpoint::open(boundary_expr)
288 : Endpoint::closed(boundary_expr);
289 const Interval interval = solution_above
290 ? Interval{finite, Endpoint::pos_inf()}
291 : Interval{Endpoint::neg_inf(), finite};
292 detail::RewriteBudget budget(context, context.limits().max_recursion_depth,
293 context.limits().max_ast_nodes, kCheckedInequalityOperation);
295 return Result<IntervalUnion>::success(IntervalUnion::from_single(interval));
296}
297
298
304
305std::shared_ptr<SymbolicExpr> rational_polynomial_expression(
306 const Polynomial<Rational>& polynomial, detail::RewriteBudget& budget) {
307 auto variable = SymbolicExpr::variable(polynomial.variable_name);
308 std::shared_ptr<SymbolicExpr> sum = SymbolicExpr::number(0);
309 for (int degree = 0; degree <= polynomial.degree(); ++degree) {
310 if (polynomial.coeffs[degree] == Rational(0)) {
311 continue;
312 }
313 auto term = SymbolicExpr::number(polynomial.coeffs[degree]);
314 if (degree > 0) {
315 auto power = degree == 1
316 ? variable
317 : SymbolicExpr::power(variable, SymbolicExpr::number(degree));
318 term = SymbolicExpr::multiply(term, power);
319 }
320 sum = SymbolicExpr::add(sum, term);
321 budget.measure(detail::node(sum));
322 }
324}
325
327 std::vector<ExactInequalityRoot>& roots, const Polynomial<Rational>& factor,
328 int multiplicity, const std::string& variable, detail::RewriteBudget& budget,
329 std::size_t& root_nodes) {
330 auto& context = budget.context();
331 auto isolated = isolate_real_roots_checked(factor, context);
332 if (!isolated) {
333 return Result<void>::failure(isolated.error());
334 }
335 const auto& intervals = isolated.value();
336 auto factor_expression = rational_polynomial_expression(factor, budget);
337 for (std::size_t index = 0; index < intervals.size(); ++index) {
339 factor, index, static_cast<std::size_t>(multiplicity), context);
340 if (!algebraic) {
341 return Result<void>::failure(algebraic.error());
342 }
343 roots.push_back(ExactInequalityRoot{
344 SymbolicExpr::root_of(
345 factor_expression, variable,
346 static_cast<int>(index)),
347 std::move(algebraic.value()),
348 multiplicity});
349 root_nodes = budget.append_size(root_nodes, budget.measure(detail::node(roots.back().expression)));
350 }
351 return Result<void>::success();
352}
353
355 std::vector<ExactInequalityRoot>& roots, ComputationContext& context) {
356 for (std::size_t i = 1; i < roots.size(); ++i) {
357 auto root = std::move(roots[i]);
358 std::size_t position = i;
359 while (position > 0) {
361 roots[position - 1].algebraic, root.algebraic, context);
362 if (!order) {
363 return Result<void>::failure(order.error());
364 }
365 if (order.value() <= 0) {
366 break;
367 }
368 roots[position] = std::move(roots[position - 1]);
369 --position;
370 }
371 roots[position] = std::move(root);
372 }
373 return Result<void>::success();
374}
375
377 const Polynomial<Rational>& polynomial,
378 const std::vector<ExactInequalityRoot>& roots, InequalityType type) {
379 const bool wants_positive =
382 const bool strict =
385 auto sign_satisfies = [&](int sign_value) {
386 return wants_positive ? sign_value > 0 : sign_value < 0;
387 };
388
389 int sign_value = rational_is_negative(polynomial.lead_coeff()) ? -1 : 1;
390 if ((polynomial.degree() & 1) != 0) {
391 sign_value = -sign_value;
392 }
393
394 std::vector<Interval> intervals;
395 Endpoint lower = Endpoint::neg_inf();
396 for (const auto& root : roots) {
397 if (sign_satisfies(sign_value)) {
398 intervals.push_back(Interval{
399 lower, Endpoint::open(root.expression)});
400 }
401 if (!strict) {
402 intervals.push_back(Interval::point(root.expression));
403 }
404 lower = Endpoint::open(root.expression);
405 if ((root.multiplicity & 1) != 0) {
406 sign_value = -sign_value;
407 }
408 }
409 if (sign_satisfies(sign_value)) {
410 intervals.push_back(Interval{lower, Endpoint::pos_inf()});
411 }
412 return intervals;
413}
414
416 const Polynomial<Rational>& polynomial,
417 InequalityType type,
419 auto factors = square_free_factorization(polynomial);
420 std::vector<ExactInequalityRoot> roots;
421 try {
422 detail::RewriteBudget budget(context, context.limits().max_recursion_depth,
423 context.limits().max_ast_nodes, kCheckedInequalityOperation);
424 std::size_t root_nodes = 0;
425 for (const auto& [factor, multiplicity] : factors) {
426 if (factor.degree() < 1) {
427 continue;
428 }
429 auto appended = append_exact_factor_roots(
430 roots, factor, multiplicity, polynomial.variable_name, budget, root_nodes);
431 if (!appended) {
432 return Result<IntervalUnion>::failure(appended.error());
433 }
434 }
436 if (!sorted) {
437 return Result<IntervalUnion>::failure(sorted.error());
438 }
439 auto intervals = exact_polynomial_sign_intervals(polynomial, roots, type);
440 check_interval_forest(intervals, budget);
441 return IntervalUnion::from_intervals_checked(std::move(intervals), context);
442 } catch (const CasError& error) {
443 return Result<IntervalUnion>::failure(error);
444 } catch (const std::length_error& error) {
447 } catch (const std::bad_alloc&) {
450 "polynomial inequality root allocation failed",
452 } catch (const std::exception& error) {
454 CasErrc::InternalInvariant, error.what(),
456 }
457
458}
459}
460
461namespace detail::inequality_support {
462
464 const Polynomial<Rational>& polynomial,
465 InequalityType type,
467 return solve_exact_affine_inequality_impl(polynomial, type, context);
468}
469
471 const Polynomial<Rational>& polynomial,
472 InequalityType type,
474 return solve_exact_polynomial_inequality_impl(polynomial, type, context);
475}
476
477}
478
479}
一元多项式模板类
std::string variable_name
变量名
bool is_zero() const
判断是否为零多项式
std::vector< CoeffType > coeffs
系数数组,coeffs[i] 为 x^i 的系数
CoeffType lead_coeff() const
获取首项系数
int degree() const
获取多项式的次数
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(T value)
ComputationContext & context() const noexcept
std::size_t measure(const std::shared_ptr< const SymbolicNode > &node)
std::size_t append_size(std::size_t current, std::size_t child)
不等式求解器 InequalitySolver,支持多项式,有理式,参数化不等式.
std::shared_ptr< SymbolicExpr > rational_polynomial_expression(const Polynomial< Rational > &polynomial, detail::RewriteBudget &budget)
std::vector< Interval > exact_polynomial_sign_intervals(const Polynomial< Rational > &polynomial, const std::vector< ExactInequalityRoot > &roots, InequalityType type)
void check_interval_forest(const std::vector< Interval > &intervals, detail::RewriteBudget &budget)
Result< IntervalUnion > solve_exact_affine_inequality_impl(const Polynomial< Rational > &polynomial, InequalityType type, ComputationContext &context)
Endpoint finite_endpoint(const std::shared_ptr< SymbolicExpr > &value, bool strict)
Result< void > verify_quadratic_boundary(const Polynomial< Rational > &polynomial, const std::shared_ptr< SymbolicExpr > &boundary, const Rational &rational_part, const Rational &radical_coefficient, const Rational &radicand, ComputationContext &context)
Result< void > append_exact_factor_roots(std::vector< ExactInequalityRoot > &roots, const Polynomial< Rational > &factor, int multiplicity, const std::string &variable, detail::RewriteBudget &budget, std::size_t &root_nodes)
Result< IntervalUnion > solve_exact_polynomial_inequality_impl(const Polynomial< Rational > &polynomial, InequalityType type, ComputationContext &context)
Result< void > sort_exact_inequality_roots(std::vector< ExactInequalityRoot > &roots, ComputationContext &context)
Result< IntervalUnion > solve_exact_polynomial_inequality(const Polynomial< Rational > &polynomial, InequalityType type, ComputationContext &context)
Result< IntervalUnion > solve_exact_affine_inequality(const Polynomial< Rational > &polynomial, InequalityType type, ComputationContext &context)
std::shared_ptr<::LMCAS::SymbolicExpr > make_expression_ptr(SymbolicNodePtr root)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
Result< int > compare_exact_real_algebraic(ExactRealAlgebraic lhs, ExactRealAlgebraic rhs, ComputationContext &context)
SymbolicNodePtr normalize_node(const SymbolicNodePtr &node, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
ExactRealAlgebraicResult make_exact_real_algebraic(Polynomial< Rational > polynomial, std::size_t root_index, std::size_t multiplicity, ComputationContext &context)
std::size_t std::size_t ComputationContext & context
const std::string const LimitExprPtr & point
const std::string & variable
std::vector< std::pair< Polynomial< Rational >, int > > square_free_factorization(const Polynomial< Rational > &poly)
__attribute__((visibility("default"))) ExprResult function(const std __attribute__((visibility("default"))) ExprResult finite_set(std ExprPtr upper
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
ExprResult interval(ExprPtr lower, ExprPtr upper, bool lower_closed, bool upper_closed)
const ExprPtr & lower
const std::vector< FGLMPoly > const MonomialOrder & order
static Result< QuadraticBoundaries > verified_quadratic_boundaries(const Polynomial< Rational > &polynomial, const Rational &rational_part, const Rational &denominator, const Rational &discriminant, detail::RewriteBudget &budget)
@ Rational
Rational numbers
Result< std::vector< std::pair< Rational, Rational > > > isolate_real_roots_checked(const Polynomial< Rational > &polynomial, ComputationContext &context)
InequalityType
不等式类型
ExprSetResult roots(const ExprPtr &expression, const std::string &variable, ComputationContext &context, const SolveOptions &options)
const std::string const ExprPtr & value
static Result< std::vector< Interval > > double_root_inequality_intervals(const Polynomial< Rational > &polynomial, const Rational &rational_part, bool wants_positive, bool leading_positive, bool strict, detail::RewriteBudget &budget)
const ExprPtr & denominator
数值求根:Newton-Raphson 迭代,二分法,Sturm 实根隔离.
多项式转换与精确符号多项式代数。
RootOf 恒等关系、求值及代数闭式化简。
方程求解策略调度器 solve_dispatch 及相关选项结构体.
内部 AST 定义。