LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
inequality_solver_parametric.cpp
浏览该文件的文档.
7#include <algorithm>
8#include <limits>
9#include <optional>
10
11namespace LMCAS {
12namespace {
13
14constexpr const char* kParametricOperation = "solve_parametric_inequality_checked";
15
17
19 if (!result) { return result; }
20 budget.consume();
21 std::size_t nodes = 0;
22 for (const auto& branch : result.value().cases) {
23 nodes = budget.append_size(nodes, budget.measure(detail::node(branch.condition)));
24 for (const auto& interval : branch.solution.intervals()) {
25 nodes = budget.append_size(nodes, budget.measure(detail::node(interval.lower.value)));
26 nodes = budget.append_size(nodes, budget.measure(detail::node(interval.upper.value)));
27 }
28 }
29 return result;
30}
31
35 "parameter signs or quadratic roots cannot be certified",
37}
38
41 ? IntervalUnion::entire_line()
42 : IntervalUnion::empty();
43}
44
45bool satisfies(int sign, InequalityType type) {
46 switch (type) {
47 case InequalityType::GreaterThan: return sign > 0;
48 case InequalityType::GreaterEqual: return sign >= 0;
49 case InequalityType::LessThan: return sign < 0;
50 case InequalityType::LessEqual: return sign <= 0;
51 }
52 return false;
53}
54
55std::shared_ptr<SymbolicExpr> sign_condition(
56 const std::shared_ptr<SymbolicExpr>& coefficient, int sign) {
57 const auto op = sign > 0 ? RelationalNode::Op::GT
58 : sign < 0 ? RelationalNode::Op::LT : RelationalNode::Op::EQ;
60 detail::node(coefficient), detail::node(SymbolicExpr::number(0)), op));
61}
62
63template <typename Node>
64Result<void> exact_operands(const Node& node, const std::string& variable,
65 const std::vector<std::string>& parameters,
66 ComputationContext& context, std::size_t depth);
67
69 const detail::SymbolicNodePtr& node, const std::string& variable,
70 const std::vector<std::string>& parameters, ComputationContext& context,
71 std::size_t depth) {
72 if (depth >= context.limits().max_recursion_depth) {
74 "parameter expression recursion limit exceeded", kParametricOperation);
75 }
76 auto step = context.consume_steps(1, kParametricOperation);
77 if (!step) return step;
78 if (const auto number = std::dynamic_pointer_cast<const NumberNode>(node)) {
80 } else if (const auto symbol = std::dynamic_pointer_cast<const VariableNode>(node)) {
81 if (!symbol->is_constant() &&
82 (symbol->name() == variable ||
83 std::find(parameters.begin(), parameters.end(), symbol->name()) != parameters.end())) {
84 return Result<void>::success();
85 }
86 } else if (const auto sum = std::dynamic_pointer_cast<const AddNode>(node)) {
87 return exact_operands(*sum, variable, parameters, context, depth);
88 } else if (const auto product = std::dynamic_pointer_cast<const MultiplyNode>(node)) {
89 return exact_operands(*product, variable, parameters, context, depth);
90 } else if (const auto power = std::dynamic_pointer_cast<const PowerNode>(node)) {
92 power->exponent(), 0, std::numeric_limits<int>::max())) {
94 power->base(), variable, parameters, context, depth + 1);
95 }
96 }
98 "parameter expression is not an exact polynomial", kParametricOperation);
99}
100
101template <typename Node>
102Result<void> exact_operands(const Node& node, const std::string& variable,
103 const std::vector<std::string>& parameters,
104 ComputationContext& context, std::size_t depth) {
105 for (const auto& operand : node.operands()) {
106 auto checked = exact_polynomial_nodes(
107 operand, variable, parameters, context, depth + 1);
108 if (!checked) return checked;
109 }
110 return Result<void>::success();
111}
112
114 const std::vector<std::shared_ptr<SymbolicExpr>>& coeffs,
116 const auto slope = detail::exact_rational_value(detail::node(coeffs[1]));
117 if (!slope) {
118 return Result<Interval>::failure(inconclusive().error());
119 }
120 if (*slope == Rational(0)) {
121 return Result<Interval>::failure(inconclusive().error());
122 }
123 std::optional<detail::AssumptionFacts> facts;
124 if (budget.context().assumptions()) { facts.emplace(*budget.context().assumptions()); }
126 detail::node(SymbolicExpr::divide(
127 SymbolicExpr::multiply(SymbolicExpr::number(-1), coeffs[0]), coeffs[1])),
128 budget, facts ? *facts : detail::no_facts()));
129
130 const bool greater = satisfies(1, type);
131 const bool strict = type == InequalityType::GreaterThan ||
133 const bool above = greater != slope->get_numerator().is_negative();
134 const auto endpoint = strict ? Endpoint::open(root)
135 : Endpoint::closed(root);
136 return above ? Interval{endpoint, Endpoint::pos_inf()}
137 : Interval{Endpoint::neg_inf(), endpoint};
138}
139
141 const std::shared_ptr<SymbolicExpr>& expr, InequalityType type,
142 const std::string& variable, ComputationContext& context) {
143 auto solved = InequalitySolver::solve_inequality_checked(expr, type, variable, context);
144 if (!solved) return PiecewiseResult::failure(solved.error());
145 PiecewiseIntervalResult result;
146 result.cases.push_back({nullptr, std::move(solved.value())});
147 return result;
148}
149
150PiecewiseResult constant_solution(const std::shared_ptr<SymbolicExpr>& coefficient,
151 InequalityType type) {
152 PiecewiseIntervalResult result;
153 for (int sign : {1, 0, -1}) {
154 result.cases.push_back({
155 sign_condition(coefficient, sign),
156 satisfies(sign, type) ? IntervalUnion::entire_line()
157 : IntervalUnion::empty()});
158 }
159 return result;
160}
161
162PiecewiseResult quadratic_solution(const std::shared_ptr<SymbolicExpr>& coefficient,
164 PiecewiseIntervalResult result;
165 for (int sign : {1, 0, -1}) {
166 auto solution = sign == 0 ? zero_polynomial_solution(type)
168 {SymbolicExpr::number(0)}, {2}, sign, type, context);
169 result.cases.push_back({sign_condition(coefficient, sign), std::move(solution)});
170 }
171 return result;
172}
174 const std::vector<std::shared_ptr<SymbolicExpr>>& coeffs,
175 int degree, InequalityType type, ComputationContext& context) {
176 if (degree == 0) return constant_solution(coeffs[0], type);
177 if (!coeffs[0]->is_zero() || !coeffs[1]->is_zero()) return inconclusive();
178 return quadratic_solution(coeffs[2], type, context);
179}
180
182 const std::shared_ptr<SymbolicExpr>& expr, const std::string& variable,
183 const std::vector<std::string>& parameters) {
184 if (parameters.empty()) return Result<void>::success();
185 if (!expr || !detail::node(expr) || variable.empty()) {
187 CasErrc::InvalidArgument, "inequality expression and variable must not be empty",
189 }
190 if (std::find(parameters.begin(), parameters.end(), variable) != parameters.end()) {
192 CasErrc::InvalidArgument, "inequality variable cannot also be a parameter",
194 }
195 return Result<void>::success();
196}
197
199 const Polynomial<SymbolicPolyCoeff>& poly, bool& symbolic,
200 detail::RewriteBudget& budget) {
201 std::vector<std::shared_ptr<SymbolicExpr>> coeffs;
202 coeffs.reserve(poly.coeffs.size());
203 std::optional<detail::AssumptionFacts> facts;
204 if (budget.context().assumptions()) { facts.emplace(*budget.context().assumptions()); }
205 std::size_t nodes = 0;
206 for (const auto& coefficient : poly.coeffs) {
208 detail::node(coefficient.val), budget, facts ? *facts : detail::no_facts()));
209 nodes = budget.append_size(nodes, budget.measure(detail::node(value)));
210 if (!detail::exact_rational_value(detail::node(value))) symbolic = true;
211 coeffs.push_back(std::move(value));
212 }
213 return coeffs;
214}
215} // namespace
216
217Result<PiecewiseIntervalResult> InequalitySolver::solve_parametric_inequality_checked(
218 const std::shared_ptr<SymbolicExpr>& expr,
219 InequalityType type,
220 const std::string& variable,
221 const std::vector<std::string>& parameters,
222 ComputationContext& context) {
223 auto arguments = check_arguments(expr, variable, parameters);
224 if (!arguments) {
225 return PiecewiseResult::failure(arguments.error());
226 }
227
228 try {
229 detail::RewriteBudget budget(context, context.limits().max_recursion_depth,
230 context.limits().max_ast_nodes, kParametricOperation);
232 if (parameters.empty()) {
233 return checked_piecewise(solved_inequality(expr, type, variable, context), budget);
234 }
235 auto converted = detail::symbolic_to_poly_checked(*expr, variable, context);
236 if (!converted) {
237 return PiecewiseResult::failure(converted.error());
238 }
239 const auto& poly = converted.value();
240 if (poly.degree() > 2) {
241 return inconclusive();
242 }
243
244 auto exact = exact_polynomial_nodes(
245 detail::node(expr), variable, parameters, context, 0);
246 if (!exact) {
247 return PiecewiseResult::failure(exact.error());
248 }
249
250 if (poly.is_zero()) {
251 PiecewiseIntervalResult result;
252 result.cases.push_back({nullptr, zero_polynomial_solution(type)});
253 return checked_piecewise(std::move(result), budget);
254 }
255
256 bool symbolic = false;
257 auto simplified = simplify_coefficients(poly, symbolic, budget);
258 if (!simplified) {
259 return PiecewiseResult::failure(simplified.error());
260 }
261 const auto& coeffs = simplified.value();
262
263 if (!symbolic) {
264 return checked_piecewise(solved_inequality(expr, type, variable, context), budget);
265 }
266
267 if (poly.degree() != 1) {
268 return checked_piecewise(nonlinear_solution(coeffs, poly.degree(), type, context), budget);
269 }
270 auto interval = linear_interval(coeffs, type, budget);
271 if (!interval) {
272 return PiecewiseResult::failure(interval.error());
273 }
274 PiecewiseIntervalResult result;
275 result.cases.push_back({nullptr, IntervalUnion::from_checked_normalized(
276 {std::move(interval.value())})});
277 return checked_piecewise(std::move(result), budget);
278 } catch (const CasError& error) {
279 return PiecewiseResult::failure(error);
280 } catch (const std::length_error& error) {
281 return PiecewiseResult::failure(CasErrc::ResourceLimit, error.what(), kParametricOperation);
282 } catch (const std::bad_alloc&) {
283 return PiecewiseResult::failure(
284 CasErrc::ResourceLimit, "parametric inequality allocation failed",
285 kParametricOperation);
286 } catch (const std::exception& error) {
287 return PiecewiseResult::failure(
288 CasErrc::InternalInvariant, error.what(), kParametricOperation);
289 }
290}
291
292Result<PiecewiseIntervalResult> InequalitySolver::solve_parametric_inequality_checked(
293 const std::shared_ptr<SymbolicExpr>& expr,
294 InequalityType type,
295 const std::string& variable,
296 const std::vector<std::string>& parameters) {
298 return solve_parametric_inequality_checked(expr, type, variable, parameters, context);
299}
300
301} // namespace LMCAS
const std::shared_ptr< const AssumptionContext > & assumptions() const noexcept
一元多项式模板类
std::vector< CoeffType > coeffs
系数数组,coeffs[i] 为 x^i 的系数
CasError & error()
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)
PiecewiseResult constant_solution(const std::shared_ptr< SymbolicExpr > &coefficient, InequalityType type)
PiecewiseResult quadratic_solution(const std::shared_ptr< SymbolicExpr > &coefficient, InequalityType type, ComputationContext &context)
Result< void > check_arguments(const std::shared_ptr< SymbolicExpr > &expr, const std::string &variable, const std::vector< std::string > &parameters)
Result< void > exact_operands(const Node &node, const std::string &variable, const std::vector< std::string > &parameters, ComputationContext &context, std::size_t depth)
std::shared_ptr< SymbolicExpr > sign_condition(const std::shared_ptr< SymbolicExpr > &coefficient, int sign)
Result< std::vector< std::shared_ptr< SymbolicExpr > > > simplify_coefficients(const Polynomial< SymbolicPolyCoeff > &poly, bool &symbolic, detail::RewriteBudget &budget)
PiecewiseResult nonlinear_solution(const std::vector< std::shared_ptr< SymbolicExpr > > &coeffs, int degree, InequalityType type, ComputationContext &context)
Result< Interval > linear_interval(const std::vector< std::shared_ptr< SymbolicExpr > > &coeffs, InequalityType type, detail::RewriteBudget &budget)
PiecewiseResult checked_piecewise(PiecewiseResult result, detail::RewriteBudget &budget)
Result< void > exact_polynomial_nodes(const detail::SymbolicNodePtr &node, const std::string &variable, const std::vector< std::string > &parameters, ComputationContext &context, std::size_t depth)
PiecewiseResult solved_inequality(const std::shared_ptr< SymbolicExpr > &expr, InequalityType type, const std::string &variable, ComputationContext &context)
IntervalUnion build_parametric_intervals(const std::vector< std::shared_ptr< SymbolicExpr > > &symbolic_roots, const std::vector< int > &multiplicities, int leading_sign, InequalityType type, ComputationContext &context)
Result< Polynomial< SymbolicPolyCoeff > > symbolic_to_poly_checked(const SymbolicExpr &expression, const std::string &variable, ComputationContext &context)
std::shared_ptr<::LMCAS::SymbolicExpr > make_expression_ptr(SymbolicNodePtr root)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
std::shared_ptr< const SymbolicNode > SymbolicNodePtr
const FactsQuery & no_facts() noexcept
std::shared_ptr< const Node > make_node(Args &&... args)
std::optional< Rational > exact_rational_value(const NumberNode &number)
SymbolicNodePtr normalize_node(const SymbolicNodePtr &node, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
const std::string & variable
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
ExprResult interval(ExprPtr lower, ExprPtr upper, bool lower_closed, bool upper_closed)
const ExprPtr RelationOp op
std::size_t std::size_t depth
std::optional< int > exact_small_integer_node(const detail::SymbolicNodePtr &node, int min_value, int max_value)
InequalityType
不等式类型
const std::string const ExprPtr & value
内部 AST 定义。