LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
solve_transcendental.cpp
浏览该文件的文档.
1#include <stdexcept>
8#include "poly_utils.hpp"
10#include "solve_strategies.hpp"
12#include <algorithm>
13
14namespace LMCAS {
15namespace {
16
17using namespace detail::transcendental;
19
20SolveResult inconclusive(const char* message) {
21 return SolveResult::failure(CasErrc::Inconclusive, message, "solve.transcendental");
22}
23
24SolveResult solve_transcendental_impl(
25 const ExprPtr& original, const ExprPtr& expr, const std::string& var,
27
29 const ExprPtr& original, const ExprPtr& inner, const ExprPtr& value,
30 const std::string& var, ComputationContext& context,
31 const SolveOptions& options, int depth) try {
32 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
33 (context).limits().max_ast_nodes, "transform");
34 auto variable = std::dynamic_pointer_cast<const VariableNode>(detail::node(inner));
35 if (variable && !variable->is_constant() && variable->name() == var) {
37 }
38 const auto product =
39 std::dynamic_pointer_cast<const MultiplyNode>(detail::node(inner));
40 if (product && product->operands().size() == 2) {
41 const auto& first = product->operands()[0];
42 const auto& second = product->operands()[1];
43 const auto coefficient =
44 std::dynamic_pointer_cast<const NumberNode>(first);
45 const auto product_variable =
46 std::dynamic_pointer_cast<const VariableNode>(second);
47 if (coefficient && coefficient->is_negative_one() &&
48 product_variable && !product_variable->is_constant() &&
49 product_variable->name() == var) {
50 auto inverted = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::multiply(
51 SymbolicExpr::number(-1), value)), transform_budget));
53 FiniteSolution{std::move(inverted), 1, {}}}}};
54 }
55 }
56 auto equation = SymbolicExpr::add(inner,
57 SymbolicExpr::multiply(value, SymbolicExpr::number(-1)));
58 auto polynomial = symbolic_to_poly<SymbolicPolyCoeff>(equation, var);
59 if (polynomial) return solve_equation(equation, var, context, options);
60 if (polynomial.error().code != CasErrc::UnsupportedExpression) {
61 return SolveResult::failure(polynomial.error());
62 }
63 return solve_transcendental_impl(original, equation, var, context, options, depth + 1);
64} catch (const CasError& error) {
65 return SolveResult::failure(error);
66} catch (const std::bad_alloc&) {
67 return SolveResult::failure(CasErrc::ResourceLimit, "allocation failed", "solve_inner_equation");
68} catch (const std::length_error& error) {
69 return SolveResult::failure(CasErrc::ResourceLimit, error.what(), "solve_inner_equation");
70} catch (const std::exception& error) {
71 return SolveResult::failure(CasErrc::InternalInvariant, error.what(), "solve_inner_equation");
72}
73
75 for (auto& candidate : finite.values) {
76 auto duplicate = std::find_if(prior.values.begin(), prior.values.end(),
77 [&](const FiniteSolution& previous) {
78 return candidate.conditions.empty() && previous.conditions.empty() &&
79 detail::node(candidate.value)->equals(*detail::node(previous.value));
80 });
81 if (duplicate == prior.values.end()) { prior.values.push_back(std::move(candidate)); }
82 }
83}
84
86 FiniteSolutions* prior, SolutionSet& output) {
87 if (prior) {
88 ParametricSolutions promoted;
89 for (auto& point : prior->values) {
90 promoted.values.push_back({std::move(point.value), {}, std::move(point.conditions)});
91 }
92 output = std::move(promoted);
93 }
94 auto& result = std::get<ParametricSolutions>(output).values;
95 if (finite) {
96 for (auto& point : finite->values) {
97 result.push_back({std::move(point.value), {}, std::move(point.conditions)});
98 }
99 } else {
100 for (auto& family : families->values) { result.push_back(std::move(family)); }
101 }
102}
104 const ExprPtr& original, const std::string& var) {
105 if (std::holds_alternative<EmptySolutions>(solved) ||
106 std::holds_alternative<UniversalSolutions>(output)) { return; }
107 if (std::holds_alternative<EmptySolutions>(output) ||
108 std::holds_alternative<UniversalSolutions>(solved)) {
109 output = std::move(solved);
110 return;
111 }
112 auto* finite = std::get_if<FiniteSolutions>(&solved);
113 auto* prior = std::get_if<FiniteSolutions>(&output);
114 if (finite && prior) {
115 append_finite_union(*finite, *prior);
116 return;
117 }
118 auto* families = std::get_if<ParametricSolutions>(&solved);
119 if ((finite || families) && (prior || std::holds_alternative<ParametricSolutions>(output))) {
120 append_parametric_union(finite, families, prior, output);
121 return;
122 }
123 output = conditional_preimage(original, var);
124}
125
127 auto difference = detail::simplify_expression(SymbolicExpr::add(left,
128 SymbolicExpr::multiply(SymbolicExpr::number(-1), right)), context);
129 if (!difference) { return Result<bool>::failure(difference.error()); }
130 auto proof = check_zero_residual(difference.value(), context);
131 if (!proof) { return Result<bool>::failure(proof.error()); }
132 return std::holds_alternative<ProvedZeroResidual>(proof.value());
133}
134
136 if (right.integer_parameters.size() != 1 || left.conditions.size() != right.conditions.size()) {
137 return false;
138 }
139 for (std::size_t c = 0; c < left.conditions.size(); ++c) {
141 left.integer_parameters.front()) ||
143 right.integer_parameters.front()) ||
144 !detail::node(left.conditions[c])->equals(*detail::node(right.conditions[c]))) {
145 return false;
146 }
147 }
148 return true;
149}
150
152 const detail::AffineForm& second, const std::string& parameter, ComputationContext& context) {
153 using MergeResult = Result<std::optional<ExprPtr>>;
154 auto half = detail::simplify_expression(SymbolicExpr::multiply(
155 SymbolicExpr::number(Rational(1, 2)), first.slope), context);
156 if (!half) { return MergeResult::failure(half.error()); }
157 const auto& half_period = half.value();
158 auto next_offset = SymbolicExpr::add(first.offset, half_period);
159 auto prior_offset = SymbolicExpr::add(second.offset, half_period);
160 ExprPtr offset;
161 auto forward = zero_difference(next_offset, second.offset, context);
162 if (!forward) { return MergeResult::failure(forward.error()); }
163 auto backward = zero_difference(prior_offset, first.offset, context);
164 if (!backward) { return MergeResult::failure(backward.error()); }
165 if (forward.value()) { offset = second.offset; }
166 else if (backward.value()) { offset = first.offset; }
167 else { return std::optional<ExprPtr>{}; }
168 auto merged = detail::simplify_expression(SymbolicExpr::add(offset,
169 SymbolicExpr::multiply(half_period, SymbolicExpr::variable(parameter))), context);
170 if (!merged) { return MergeResult::failure(merged.error()); }
171 return std::optional<ExprPtr>{std::move(merged.value())};
172}
173
176 if (!compatible_periodic_conditions(left, right)) { return false; }
177 auto first = detail::recognize_affine(*left.value, left.integer_parameters.front(), context);
178 if (!first) { return Result<bool>::failure(first.error()); }
179 auto second = detail::recognize_affine(*right.value, right.integer_parameters.front(), context);
180 if (!second) { return Result<bool>::failure(second.error()); }
181 if (!first.value() || !second.value()) { return false; }
182 for (auto* coefficient : {&first.value()->slope, &first.value()->offset,
183 &second.value()->slope, &second.value()->offset}) {
184 auto normalized = detail::simplify_expression(*coefficient, context);
185 if (!normalized) { return Result<bool>::failure(normalized.error()); }
186 *coefficient = std::move(normalized.value());
187 }
188 auto equal_slopes = zero_difference(first.value()->slope, second.value()->slope, context);
189 if (!equal_slopes) { return Result<bool>::failure(equal_slopes.error()); }
190 if (!equal_slopes.value()) { return false; }
191 auto merged = merged_half_period_value(
192 *first.value(), *second.value(), left.integer_parameters.front(), context);
193 if (!merged) { return Result<bool>::failure(merged.error()); }
194 if (!merged.value()) { return false; }
195 left.value = std::move(*merged.value());
196 return true;
197}
198
200 auto* parametric = std::get_if<ParametricSolutions>(&solutions);
201 if (!parametric) { return Result<void>::success(); }
202 auto& families = parametric->values;
203 for (std::size_t i = 0; i < families.size(); ++i) {
204 if (families[i].integer_parameters.size() != 1) { continue; }
205 for (std::size_t j = i + 1; j < families.size();) {
206 auto merged = coalesce_periodic_pair(families[i], families[j], context);
207 if (!merged) { return Result<void>::failure(merged.error()); }
208 if (!merged.value()) { ++j; continue; }
209 families.erase(families.begin() + j);
210 j = i + 1;
211 }
212 }
213 return Result<void>::success();
214}
215
217 const ExprPtr& original, const ExprPtr& inner,
218 const std::vector<ExprPtr>& values, const std::string& var,
220 if (values.empty()) return inconclusive("Inverse did not establish a complete value set");
221 SolutionSet results = EmptySolutions{};
222 for (const auto& value : values) {
223 auto solved = solve_inner_equation(original, inner, value, var, context, options, depth);
224 if (!solved) return solved;
225 append_solutions(std::move(solved).value(), results, original, var);
226 }
227 return results;
228}
229
231 const ExprPtr& original, const SubstitutionResult& substitution, const std::string& var,
233 auto polynomial = symbolic_to_poly<SymbolicPolyCoeff>(
234 substitution.poly_in_u, substitution.u_var);
235 if (!polynomial) {
236 if (polynomial.error().code == CasErrc::UnsupportedExpression) {
237 return inconclusive("Substitution is not a supported polynomial");
238 }
239 return SolveResult::failure(polynomial.error());
240 }
241 auto solved = solve_equation(substitution.poly_in_u, substitution.u_var, context, options);
242 if (!solved) return solved;
243 if (std::holds_alternative<EmptySolutions>(solved.value())) return solved;
244 auto finite = std::get_if<FiniteSolutions>(&solved.value());
245 if (!finite) return conditional_preimage(original, var);
246 SolutionSet results = EmptySolutions{};
247 for (const auto& value : finite->values) {
248 if (!value.conditions.empty()) return conditional_preimage(original, var);
249 auto inverted = solve_inner_equation(
250 original, substitution.u_expr, value.value, var, context, options, depth);
251 if (!inverted) return inverted;
252 append_solutions(std::move(inverted).value(), results, original, var);
253 }
254 return results;
255}
256
258 const std::string& var, ComputationContext& context) try {
259 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
260 (context).limits().max_ast_nodes, "transform");
261 std::optional<detail::AssumptionFacts> assumptions;
262 if (context.assumptions()) { assumptions.emplace(*context.assumptions()); }
263 const LMCAS::FactsQuery& facts = assumptions ?
264 static_cast<const LMCAS::FactsQuery&>(*assumptions) : detail::no_facts();
265 auto nonzero = detail::query_nonzero_value(detail::normalize_node(detail::node(expr), transform_budget), facts, Domain::Real, context);
266 if (!nonzero) { return SolveResult::failure(nonzero.error()); }
267 if (nonzero.value() == Tribool::True) { return SolutionSet{EmptySolutions{}}; }
268 if (nonzero.value() == Tribool::False) { return SolutionSet{UniversalSolutions{}}; }
269 return conditional_preimage(original, var);
270} catch (const CasError& error) {
271 return SolveResult::failure(error);
272} catch (const std::bad_alloc&) {
273 return SolveResult::failure(CasErrc::ResourceLimit, "allocation failed", "solve_transcendental_constant");
274} catch (const std::length_error& error) {
275 return SolveResult::failure(CasErrc::ResourceLimit, error.what(), "solve_transcendental_constant");
276} catch (const std::exception& error) {
277 return SolveResult::failure(CasErrc::InternalInvariant, error.what(), "solve_transcendental_constant");
278}
279
281 const std::string& var, ComputationContext& context, const SolveOptions& options, int depth) {
282 auto inverted = invert_function(pattern, original, var, context, options);
283 if (!inverted) { return inverted; }
284 auto finite = std::get_if<FiniteSolutions>(&inverted.value());
285 if (!finite) { return inverted; }
286 SolutionSet results = EmptySolutions{};
287 for (const auto& value : finite->values) {
288 if (!value.conditions.empty()) { return conditional_preimage(original, var); }
289 auto solved = solve_inner_equation(original, pattern.inner, value.value,
290 var, context, options, depth);
291 if (!solved) { return solved; }
292 append_solutions(std::move(solved).value(), results, original, var);
293 }
294 return results;
295}
296
298 const ExprPtr& original, const ExprPtr& expr, const std::string& var,
300 auto step = context.consume_steps(1, "solve.transcendental");
301 if (!step) { return SolveResult::failure(step.error()); }
302 if (depth > maximum_transcendental_depth) { return inconclusive("Transcendental inversion depth exceeded"); }
304 return solve_transcendental_constant(original, expr, var, context);
305 }
306 if (auto pattern = decompose_lambert_w_pattern(expr, var, context)) {
307 return solve_inverted_values(original, pattern->inner, invert_lambert_w(pattern->rhs, context),
308 var, context, options, depth);
309 }
310 if (auto pattern = decompose_exp_base_pattern(expr, var, context)) {
311 return solve_inverted_values(original, pattern->inner,
312 invert_exp_base(pattern->base, pattern->rhs, context), var, context, options, depth);
313 }
314 if (auto pattern = decompose_trig_exp_pattern(expr, var)) {
315 return solve_function_pattern(*pattern, original, var, context, options, depth);
316 }
317 auto substitution = detect_substitution(expr, var);
318 if (!substitution) {
319 return SolveResult::failure(substitution.error());
320 }
321 if (substitution.value()) {
322 return solve_substitution(original, *substitution.value(), var, context, options, depth);
323 }
324 return inconclusive("No complete transcendental inversion matched");
325}
326
327}
328
330 const ExprPtr& expr, const std::string& var,
332 if (!expr || !detail::node(expr) || var.empty()) {
334 "Expression and variable must not be empty", "solve.transcendental");
335 }
336 detail::RewriteBudget transform_budget(context, context.limits().max_recursion_depth,
337 context.limits().max_ast_nodes,
338 "solve.transcendental");
339 auto equation = expr;
340 if (auto relation = std::dynamic_pointer_cast<const RelationalNode>(detail::node(expr))) {
341 if (relation->op() != RelationalNode::Op::EQ) {
343 "Expected an equality", "solve.transcendental");
344 }
345 equation = SymbolicExpr::add(detail::make_expression_ptr(relation->left()),
346 SymbolicExpr::multiply(SymbolicExpr::number(-1),
348 }
349 equation = detail::make_expression_ptr(detail::normalize_node(detail::node(equation), transform_budget));
350 auto solved = std::dynamic_pointer_cast<const MultiplyNode>(detail::node(equation)) ?
351 solve_equation(equation, var, context, options) :
352 solve_transcendental_impl(expr, equation, var, context, options, 0);
353 if (!solved) return solved;
354 auto coalesced = coalesce_periodic_families(solved.value(), context);
355 if (!coalesced) return SolveResult::failure(coalesced.error());
356 return detail::finalize_solution_set(expr, var, std::move(solved).value(), context, Domain::Real, options);
357}
358catch (const CasError& error) {
359 return SolveResult::failure(error);
360} catch (const std::bad_alloc&) {
362 "transcendental solve allocation failed", "solve.transcendental");
363} catch (const std::length_error& error) {
364 return SolveResult::failure(CasErrc::ResourceLimit, error.what(), "solve.transcendental");
365} catch (const std::exception& error) {
366 return SolveResult::failure(CasErrc::InternalInvariant, error.what(), "solve.transcendental");
367}
368
370 const ExprPtr& expr, const std::string& var, const SolveOptions& options) {
372 return solve_transcendental(expr, var, context, options);
373}
374
375}
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(T value)
SolveResult solve_function_pattern(const InversePattern &pattern, const ExprPtr &original, const std::string &var, ComputationContext &context, const SolveOptions &options, int depth)
SolveResult solve_inner_equation(const ExprPtr &original, const ExprPtr &inner, const ExprPtr &value, const std::string &var, ComputationContext &context, const SolveOptions &options, int depth)
bool compatible_periodic_conditions(const ParametricSolution &left, const ParametricSolution &right)
void append_solutions(SolutionSet solved, SolutionSet &output, const ExprPtr &original, const std::string &var)
SolveResult solve_inverted_values(const ExprPtr &original, const ExprPtr &inner, const std::vector< ExprPtr > &values, const std::string &var, ComputationContext &context, const SolveOptions &options, int depth)
Result< std::optional< ExprPtr > > merged_half_period_value(const detail::AffineForm &first, const detail::AffineForm &second, const std::string &parameter, ComputationContext &context)
void append_parametric_union(FiniteSolutions *finite, ParametricSolutions *families, FiniteSolutions *prior, SolutionSet &output)
Result< bool > zero_difference(const ExprPtr &left, const ExprPtr &right, ComputationContext &context)
SolveResult solve_transcendental_constant(const ExprPtr &original, const ExprPtr &expr, const std::string &var, ComputationContext &context)
SolveResult solve_substitution(const ExprPtr &original, const SubstitutionResult &substitution, const std::string &var, ComputationContext &context, const SolveOptions &options, int depth)
SolveResult solve_transcendental_impl(const ExprPtr &original, const ExprPtr &expr, const std::string &var, ComputationContext &context, const SolveOptions &options, int depth)
void append_finite_union(FiniteSolutions &finite, FiniteSolutions &prior)
Result< void > coalesce_periodic_families(SolutionSet &solutions, ComputationContext &context)
Result< bool > coalesce_periodic_pair(ParametricSolution &left, const ParametricSolution &right, ComputationContext &context)
std::optional< LambertWPattern > decompose_lambert_w_pattern(const std::shared_ptr< SymbolicExpr > &expr, const std::string &var, ComputationContext &context)
std::optional< ExpBasePattern > decompose_exp_base_pattern(const std::shared_ptr< SymbolicExpr > &expr, const std::string &var, ComputationContext &context)
std::vector< std::shared_ptr< SymbolicExpr > > invert_lambert_w(const std::shared_ptr< SymbolicExpr > &c, ComputationContext &context)
SolutionSet conditional_preimage(const ExprPtr &original, const std::string &var)
std::optional< InversePattern > decompose_trig_exp_pattern(const std::shared_ptr< SymbolicExpr > &expr, const std::string &var)
SolveResult invert_function(const InversePattern &pattern, const ExprPtr &original, const std::string &var, ComputationContext &context, const SolveOptions &options)
std::vector< std::shared_ptr< SymbolicExpr > > invert_exp_base(const std::shared_ptr< SymbolicExpr > &base, const std::shared_ptr< SymbolicExpr > &c, ComputationContext &context)
std::shared_ptr<::LMCAS::SymbolicExpr > make_expression_ptr(SymbolicNodePtr root)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
ExpressionResult simplify_expression(const ExprPtr &expression, ComputationContext &context)
SolveResult finalize_solution_set(const ExprPtr &original, const std::string &variable, SolutionSet solutions, ComputationContext &context, Domain domain, const SolveOptions &options)
std::shared_ptr< SymbolicExpr > slope
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)
Result< std::optional< AffineForm > > recognize_affine(const SymbolicExpr &expression, const std::string &variable, ComputationContext &context)
std::shared_ptr< SymbolicExpr > offset
const std::string const LimitExprPtr & point
const std::string & variable
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
Result< SolutionSet > SolveResult
@ Real
Real numbers
const std::string ComputationContext const SolveOptions & options
const VectorField const VectorField & c
std::shared_ptr< SymbolicExpr > u_expr
换元表达式(u = f(x) 中的 f(x))
bool expression_depends_on_variable(const detail::SymbolicNodePtr &expression, const std::string &variable, detail::RewriteBudget *budget)
@ True
Property definitely holds
@ False
Property definitely does not hold
SolveResult solve_transcendental(const ExprPtr &expr, const std::string &var, ComputationContext &context, const SolveOptions &options)
LMCAS::SolveResult solve_equation(const std::shared_ptr< SymbolicExpr > &expr, const std::string &var, ComputationContext &context, const SolveOptions &opts)
std::vector< std::string > integer_parameters
std::vector< FiniteSolution > values
std::size_t std::size_t depth
std::shared_ptr< SymbolicExpr > ExprPtr
Result< std::optional< SubstitutionResult > > detect_substitution(const std::shared_ptr< SymbolicExpr > &expr, const std::string &var)
const ExprPtr & right
std::shared_ptr< SymbolicExpr > poly_in_u
换元后关于 u 的多项式
ResidualCheckResult check_zero_residual(const ExprPtr &residual, ComputationContext &context, const LMCAS::EqvOptions &options)
ExprResult relation(const ExprPtr &lhs, const ExprPtr &rhs, RelationOp op)
std::variant< EmptySolutions, UniversalSolutions, FiniteSolutions, IntervalSolutions, ConditionalSolutions, ParametricSolutions > SolutionSet
成功的 SolveResult 表示操作定义域内的完整解集。 完备性未决时返回外层 Inconclusive 错误;EmptySolutions 表示已证明的空解集。
const std::string const ExprPtr & value
std::vector< ParametricSolution > values
conditions 是在操作上下文假设下须同时满足的条件。 空条件保留原表达式定义域且不增加约束;整数参数遍历全体整数。
方程求解选项,控制数值求解行为和结果形式.
换元结果,记录换元表达式、换元后的多项式及换元变量名。
多项式转换与精确符号多项式代数。
方程求解策略调度器 solve_dispatch 及相关选项结构体.
超越方程求解:三角、指数、对数方程的符号反演。