LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
solve_mixed_transcendental.cpp
浏览该文件的文档.
6#include "poly_utils.hpp"
11#include "newton_raphson.hpp"
14
15#include <algorithm>
16#include <cmath>
17#include <new>
18#include <stdexcept>
19
20namespace LMCAS {
21namespace {
22
23constexpr const char* mixed_operation = "solve_mixed_transcendental_checked";
24
25std::optional<CasError> validate_mixed_input(
26 const std::shared_ptr<SymbolicExpr>& expr,
27 const std::string& var, const SolveOptions& opts) {
28 if (!expr || !detail::node(expr)) {
29 return CasError{
30 CasErrc::InvalidArgument, "待求解表达式不能为空", mixed_operation};
31 }
32 if (var.empty()) {
33 return CasError{
34 CasErrc::InvalidArgument, "求解变量不能为空", mixed_operation};
35 }
36 if (!std::isfinite(opts.tolerance) || opts.tolerance <= 0.0 ||
37 opts.max_newton_iterations <= 0 || opts.max_roots == 0 || opts.max_roots < -1) {
38 return CasError{
39 CasErrc::InvalidArgument, "求解选项包含无效界限", mixed_operation};
40 }
41 if (opts.has_search_interval &&
42 (!std::isfinite(opts.search_lo) || !std::isfinite(opts.search_hi) ||
43 opts.search_lo >= opts.search_hi)) {
44 return CasError{
45 CasErrc::InvalidArgument, "搜索区间必须为有限递增区间", mixed_operation};
46 }
47 return std::nullopt;
48}
49
51 return code != CasErrc::DomainError &&
52 code != CasErrc::UnboundSymbol &&
55}
56
58 const std::shared_ptr<SymbolicExpr>& expr,
59 bool unresolved_parameters, const SolveOptions& opts, ComputationContext& context) {
61 if (unresolved_parameters) {
64 "equation depends on unresolved parameters but not the solve variable"});
65 }
66 auto evaluated = evaluate_numeric(*expr, {}, context);
67 if (!evaluated && is_mixed_terminal_error(evaluated.error().code)) {
68 return MixedTranscendentalResult::failure(evaluated.error());
69 }
70 if (!evaluated || !std::isfinite(evaluated.value().value)) {
73 "constant equation could not be evaluated in the real domain"});
74 }
75 if (std::abs(evaluated.value().value) <= opts.tolerance) {
78 "equation is identically zero; every real value satisfies it"});
79 }
81}
82
84public:
85 MixedRootCollection(const std::shared_ptr<SymbolicExpr>& expr,
86 const std::string& var, const SearchInterval& interval,
88 : expr_(expr), var_(var), interval_(interval), opts_(opts), context_(context),
89 budget_(context, context.limits().max_recursion_depth,
90 context.limits().max_ast_nodes, mixed_operation) {}
91
92 std::optional<CasError> solve(ComputationContext& context) {
93 auto sum = std::dynamic_pointer_cast<const AddNode>(detail::node(expr_));
94 const bool literal_offset = sum && sum->operands().size() == 2 &&
95 std::any_of(sum->operands().begin(), sum->operands().end(),
96 [](const auto& operand) {
97 return dynamic_cast<const NumberNode*>(operand.get()) != nullptr;
98 });
99 auto pattern = literal_offset
101 : std::nullopt;
102 auto variable = pattern
103 ? std::dynamic_pointer_cast<const VariableNode>(detail::node(pattern->inner))
104 : nullptr;
105 auto coefficient = pattern && pattern->coefficient
106 ? detail::exact_rational_value(detail::node(pattern->coefficient))
107 : std::nullopt;
108 if (pattern && pattern->func_type == FunctionNode::FuncType::Exp &&
109 variable && !variable->is_constant() && variable->name() == var_ &&
110 coefficient && (*coefficient == Rational(1) || *coefficient == Rational(-1))) {
112 *pattern, expr_, var_, context, opts_);
113 if (!inverted) return inverted.error();
114 auto finite = std::get_if<FiniteSolutions>(&inverted.value());
115 if (finite && finite->values.size() == 1 &&
116 finite->values.front().conditions.empty()) {
117 const auto& target = finite->values.front().value;
118 auto logarithm = std::dynamic_pointer_cast<const FunctionNode>(detail::node(target));
119 if (logarithm && logarithm->type() == FunctionNode::FuncType::Ln) {
120 // Keep the symbolic real preimage instead of rounding it and
121 // testing cancellation between exponentially large values.
122 exponential_inner_ = pattern->inner;
124 if (accept_symbolic(target)) return failure_;
125 if (failure_) return failure_;
126 }
127 }
128 }
129 auto factored = factor_transcendental(expr_, var_, context);
130 if (!factored) return factored.error();
131 auto factors = std::move(factored.value());
132 if (factors.empty()) {
133 factors.push_back(expr_);
134 complete_ = false;
135 }
136 for (const auto& factor : factors) {
137 auto step = context.consume_steps(1, mixed_operation);
138 if (!step) {
139 return step.error();
140 }
141 auto failure = solve_factor(factor, context);
142 if (failure) {
143 return failure;
144 }
145 if (failure_) return failure_;
146 }
147 return std::nullopt;
148 }
149
151 struct RepresentedRoot {
152 lmmc_real_t value;
153 std::shared_ptr<SymbolicExpr> expression;
154 bool symbolic;
155 };
156 auto numeric_values = deduplicate_roots(roots_, opts_.tolerance, -1);
157 std::vector<RepresentedRoot> values;
158 values.reserve(symbolic_roots_.size() + numeric_values.size());
159 for (auto& root : symbolic_roots_) {
160 values.push_back({root.value, std::move(root.expression), true});
161 }
162 for (const auto value : numeric_values) {
163 values.push_back(
164 {value, SymbolicExpr::number(static_cast<double>(value)), false});
165 }
166 std::sort(values.begin(), values.end(),
167 [](const RepresentedRoot& left, const RepresentedRoot& right) {
168 if (left.value != right.value) { return left.value < right.value; }
169 return left.symbolic && !right.symbolic;
170 });
171 const auto threshold = 10.0 * opts_.tolerance;
172 std::vector<RepresentedRoot> unique;
173 unique.reserve(values.size());
174 for (auto& root : values) {
175 if (!unique.empty() &&
176 std::fabs(root.value - unique.back().value) < threshold) {
177 if (root.symbolic && !unique.back().symbolic) {
178 unique.back() = std::move(root);
179 }
180 continue;
181 }
182 unique.push_back(std::move(root));
183 }
184 if (opts_.max_roots > 0 &&
185 unique.size() > static_cast<std::size_t>(opts_.max_roots)) {
186 unique.resize(static_cast<std::size_t>(opts_.max_roots));
187 complete_ = false;
188 }
189 std::vector<std::shared_ptr<SymbolicExpr>> results;
190 results.reserve(unique.size());
191 for (auto& root : unique) {
192 results.push_back(std::move(root.expression));
193 }
194 return MixedTranscendentalResult::success(
195 MathResult<std::vector<std::shared_ptr<SymbolicExpr>>>{
196 std::move(results),
197 complete_ ? Completeness::Complete : Completeness::Inconclusive,
198 complete_ ? std::string{} : "有界数值隔离未能证明排除全部未检测根"});
199 }
200
201private:
202 void polish_value(lmmc_real_t& value, lmmc_real_t& residual, int& iterations) {
203 auto polish = opts_;
204 polish.has_initial_guess = true;
205 polish.initial_guess = value;
206 auto refined = solve_numeric_checked(expr_, var_, context_, polish);
207 if (!refined) {
208 if (is_mixed_terminal_error(refined.error().code)) {
209 failure_ = refined.error();
210 }
211 return;
212 }
213 for (const auto& candidate : refined.value()) {
214 if (candidate.value < interval_.lo || candidate.value > interval_.hi) { continue; }
215 const auto candidate_residual = std::fabs(
216 detail::mixed_evaluate_at(expr_, var_, candidate.value, budget_));
217 if (std::isfinite(candidate_residual) && candidate_residual < residual) {
218 value = candidate.value;
219 residual = candidate_residual;
220 iterations += candidate.iterations;
221 }
222 }
223 }
224
225 bool accept_value(lmmc_real_t value, int iterations) {
226 if (!std::isfinite(value)) { complete_ = false; return false; }
227 if (value < interval_.lo || value > interval_.hi) return true;
228 auto residual = std::fabs(detail::mixed_evaluate_at(expr_, var_, value, budget_));
229 if (std::isfinite(residual) && residual > opts_.tolerance) {
230 polish_value(value, residual, iterations);
231 }
232 if (!std::isfinite(residual) || residual > opts_.tolerance) {
233 complete_ = false;
234 return false;
235 }
236 roots_.push_back({value, residual, iterations});
237 return true;
238 }
239
241 if (error.code == CasErrc::DomainError) {
242 return true;
243 }
244 if (is_mixed_terminal_error(error.code)) {
245 failure_ = error;
246 }
247 complete_ = false;
248 return false;
249 }
250
251 std::optional<bool> accept_symbolic_witness(
252 std::shared_ptr<SymbolicExpr> simplified,
253 lmmc_real_t value, bool inverse_identity) {
254 auto bound = detail::substitute_raw(expr_, var_, simplified);
255 std::optional<detail::AssumptionFacts> assumptions;
256 if (context_.assumptions()) {
257 assumptions.emplace(*context_.assumptions());
258 }
259 const FactsQuery& facts = assumptions
260 ? static_cast<const FactsQuery&>(*assumptions)
262 auto defined = detail::query_definedness(
263 detail::node(bound), facts, Domain::Real, context_);
264 if (!defined) {
265 if (is_mixed_terminal_error(defined.error().code)) {
266 failure_ = defined.error();
267 return false;
268 }
269 return std::nullopt;
270 }
271 if (defined.value() == Tribool::False) {
272 return true;
273 }
274 if (defined.value() != Tribool::True) {
275 return std::nullopt;
276 }
277
278 auto residual = check_zero_residual(bound, context_);
279 if (!residual) {
280 if (is_mixed_terminal_error(residual.error().code)) {
281 failure_ = residual.error();
282 return false;
283 }
284 return std::nullopt;
285 }
286 if (!std::holds_alternative<ProvedZeroResidual>(
287 residual.value())) {
288 if (!inverse_identity) return std::nullopt;
289 }
290 symbolic_roots_.push_back(
291 SymbolicRoot{value, std::move(simplified)});
292 return true;
293 }
294
295 bool accept_symbolic(const std::shared_ptr<SymbolicExpr>& root) {
296 if (!root) {
297 complete_ = false;
298 return false;
299 }
300 bool inverse_identity = false;
301 if (exponential_inner_) {
303 var_, detail::node(root), &budget_);
304 budget_.measure(argument);
306 inverse_identity = argument->equals(*detail::node(exponential_target_));
307 }
308 auto simplified = inverse_identity ? root : detail::make_expression_ptr(
310 if (!simplified || !detail::node(simplified)) {
311 complete_ = false;
312 return false;
313 }
314 auto evaluated = evaluate_numeric(*simplified, {}, context_);
315 if (!evaluated) {
316 return handle_symbolic_evaluation_error(evaluated.error());
317 }
318 const auto value = evaluated.value().value;
319 if (!std::isfinite(value)) {
320 complete_ = false;
321 return false;
322 }
324 return true;
325 }
326 auto accepted =
327 accept_symbolic_witness(std::move(simplified), value, inverse_identity);
328 if (accepted) {
329 return *accepted;
330 }
331 return accept_value(value, 0);
332 }
333
335 const FiniteSolutions& solutions) {
336 bool handled = true;
337 for (const auto& solution : solutions.values) {
338 if (!solution.conditions.empty()) {
339 complete_ = false;
340 handled = false;
341 } else if (!accept_symbolic(solution.value)) {
342 handled = false;
343 }
344 if (failure_) {
346 }
347 }
348 return handled;
349 }
350
352 if (contains_transcendental_of_var(factor, var_)) return false;
353 auto polynomial = symbolic_to_poly<SymbolicPolyCoeff>(factor, var_);
354 if (!polynomial) {
355 if (polynomial.error().code != CasErrc::UnsupportedExpression)
356 return Result<bool>::failure(polynomial.error());
357 complete_ = false;
358 return false;
359 }
360 auto solved = solve_equation(factor, var_, context, opts_);
361 if (!solved) {
362 if (solved.error().code != CasErrc::Inconclusive)
363 return Result<bool>::failure(solved.error());
364 complete_ = false;
365 return false;
366 }
367 if (std::holds_alternative<EmptySolutions>(solved.value())) return true;
368 auto finite = std::get_if<FiniteSolutions>(&solved.value());
369 if (!finite) {
370 complete_ = false;
371 return false;
372 }
373 return accept_finite_solutions(*finite);
374 }
375
376 std::optional<CasError> check_constant_factor(const ExprPtr& factor) {
377 std::optional<detail::AssumptionFacts> assumptions;
378 if (context_.assumptions()) { assumptions.emplace(*context_.assumptions()); }
379 const FactsQuery& facts = assumptions ? static_cast<const FactsQuery&>(*assumptions) : detail::no_facts();
380 auto nonzero = detail::query_nonzero_value(detail::node(factor), facts, Domain::Real, context_);
381 if (!nonzero) { return nonzero.error(); }
382 if (nonzero.value() != Tribool::True) { complete_ = false; }
383 return std::nullopt;
384 }
385
387 const ExprPtr& factor, ComputationContext& context) {
388 auto symbolic =
390 if (!symbolic) {
391 if (symbolic.error().code != CasErrc::Inconclusive) {
392 return Result<bool>::failure(symbolic.error());
393 }
394 complete_ = false;
395 return false;
396 }
397 if (std::holds_alternative<EmptySolutions>(symbolic.value())) {
398 return true;
399 }
400 if (auto finite =
401 std::get_if<FiniteSolutions>(&symbolic.value())) {
402 return accept_finite_solutions(*finite);
403 }
404 complete_ = false;
405 return false;
406 }
407
408 std::optional<CasError> solve_factor(
409 const std::shared_ptr<SymbolicExpr>& factor,
411 if (!factor || !detail::node(factor)) {
412 complete_ = false;
413 return std::nullopt;
414 }
416 return check_constant_factor(factor);
417 }
418 auto polynomial = solve_polynomial_factor(factor, context);
419 if (!polynomial) {
420 return polynomial.error();
421 }
422 if (polynomial.value()) {
423 return std::nullopt;
424 }
425 auto symbolic = solve_transcendental_factor(factor, context);
426 if (!symbolic) {
427 return symbolic.error();
428 }
429 if (symbolic.value()) {
430 return std::nullopt;
431 }
432
433 auto numeric = detail::mixed_numerical_path(
434 factor, var_, interval_, opts_, budget_, complete_);
435 if (!numeric) {
436 return std::move(numeric.error());
437 }
438 for (const auto& root : numeric.value()) {
439 accept_value(root.value, root.iterations);
440 }
441 return std::nullopt;
442 }
443
445 lmmc_real_t value;
446 std::shared_ptr<SymbolicExpr> expression;
447 };
448
449 const std::shared_ptr<SymbolicExpr>& expr_;
450 std::shared_ptr<SymbolicExpr> exponential_inner_;
451 std::shared_ptr<SymbolicExpr> exponential_target_;
452 const std::string& var_;
457 std::optional<CasError> failure_;
458 std::vector<NumericRoot> roots_;
459 std::vector<SymbolicRoot> symbolic_roots_;
460 bool complete_ = true;
461};
462
464 const std::shared_ptr<SymbolicExpr>& expr, const std::string& var,
466 auto step = context.consume_steps(1, mixed_operation);
467 if (!step) {
468 return MixedTranscendentalResult::failure(step.error());
469 }
470 const auto variables = free_variables(detail::node(expr));
471 if (variables.find(var) == variables.end()) {
472 return classify_constant_equation(expr, !variables.empty(), opts, context);
473 }
474 auto interval = determine_search_interval(expr, var, opts);
475 if (!interval) {
477 CasErrc::InvalidArgument, "无法确定有效搜索区间", mixed_operation);
478 }
479 if (!std::isfinite(interval->lo) || !std::isfinite(interval->hi) ||
480 interval->lo >= interval->hi) {
482 CasErrc::InvalidArgument, "搜索区间必须为有限递增区间", mixed_operation);
483 }
484 MixedRootCollection collection(expr, var, *interval, opts, context);
485 auto failure = collection.solve(context);
486 if (failure) {
487 return MixedTranscendentalResult::failure(std::move(*failure));
488 }
489 return collection.finish();
490}
491
492}
493
495 const std::shared_ptr<SymbolicExpr>& expr, const std::string& var,
497 auto invalid = validate_mixed_input(expr, var, opts);
498 if (invalid) {
499 return MixedTranscendentalResult::failure(std::move(*invalid));
500 }
501 try {
502 return solve_mixed_equation(expr, var, opts, context);
503 } catch (const CasError& error) {
505 } catch (const std::bad_alloc&) {
507 CasErrc::ResourceLimit, "混合求解分配失败", mixed_operation);
508 } catch (const std::length_error& error) {
509 return MixedTranscendentalResult::failure(CasErrc::ResourceLimit, error.what(), mixed_operation);
510 } catch (const std::exception& error) {
511 return MixedTranscendentalResult::failure(CasErrc::InternalInvariant, error.what(), mixed_operation);
512 }
513}
514
516 const std::shared_ptr<SymbolicExpr>& expr,
517 const std::string& var, const SolveOptions& opts) {
519 return solve_mixed_transcendental_checked(expr, var, opts, context);
520}
521
522}
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(MathResult< std::vector< std::shared_ptr< SymbolicExpr > > > value)
std::optional< bool > accept_symbolic_witness(std::shared_ptr< SymbolicExpr > simplified, lmmc_real_t value, bool inverse_identity)
Result< bool > solve_polynomial_factor(const ExprPtr &factor, ComputationContext &context)
std::optional< CasError > solve_factor(const std::shared_ptr< SymbolicExpr > &factor, ComputationContext &context)
Result< bool > solve_transcendental_factor(const ExprPtr &factor, ComputationContext &context)
MixedRootCollection(const std::shared_ptr< SymbolicExpr > &expr, const std::string &var, const SearchInterval &interval, const SolveOptions &opts, ComputationContext &context)
MixedTranscendentalResult classify_constant_equation(const std::shared_ptr< SymbolicExpr > &expr, bool unresolved_parameters, const SolveOptions &opts, ComputationContext &context)
MixedTranscendentalResult solve_mixed_equation(const std::shared_ptr< SymbolicExpr > &expr, const std::string &var, const SolveOptions &opts, ComputationContext &context)
std::optional< CasError > validate_mixed_input(const std::shared_ptr< SymbolicExpr > &expr, const std::string &var, const SolveOptions &opts)
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::shared_ptr<::LMCAS::SymbolicExpr > make_expression_ptr(SymbolicNodePtr root)
ExprPtr substitute_raw(const ExprPtr &expression, const std::string &variable, const ExprPtr &value)
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
Result< Tribool > query_definedness(const Node &node, const FactsQuery &facts, Domain domain, ComputationContext &context, bool *violated_real_restriction)
std::optional< Rational > exact_rational_value(const NumberNode &number)
SymbolicNodePtr normalize_node(const SymbolicNodePtr &node, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
Result< std::vector< NumericRoot > > mixed_numerical_path(const std::shared_ptr< SymbolicExpr > &factor, const std::string &var, const SearchInterval &interval, const SolveOptions &opts, RewriteBudget &budget, bool &complete)
隔离并精化单个因子的数值根。
lmmc_real_t mixed_evaluate_at(const std::shared_ptr< SymbolicExpr > &expr, const std::string &var, lmmc_real_t x, RewriteBudget &budget)
Result< MathResult< std::vector< std::shared_ptr< SymbolicExpr > > > > MixedTranscendentalResult
受检混合超越方程求解结果.
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)
@ Real
Real numbers
bool has_search_interval
是否指定了搜索区间
std::vector< lmmc_real_t > deduplicate_roots(std::vector< NumericRoot > &roots, lmmc_real_t tolerance, int max_roots)
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::optional< SearchInterval > determine_search_interval(const std::shared_ptr< SymbolicExpr > &expr, const std::string &var, const SolveOptions &opts)
lmmc_real_t tolerance
收敛容差
std::vector< FiniteSolution > values
lmmc_real_t search_lo
搜索区间下界(默认 -10)
Result< std::vector< std::shared_ptr< SymbolicExpr > > > factor_transcendental(const std::shared_ptr< SymbolicExpr > &expr, const std::string &var, ComputationContext &context)
std::set< std::string > free_variables(const detail::SymbolicNodePtr &expression, detail::RewriteBudget *budget)
std::shared_ptr< SymbolicExpr > ExprPtr
const ExprPtr & target
const ExprPtr & right
lmmc_real_t search_hi
搜索区间上界(默认 10)
ResidualCheckResult check_zero_residual(const ExprPtr &residual, ComputationContext &context, const LMCAS::EqvOptions &options)
const std::string const ExprPtr & value
int max_roots
数值候选数量预算;精确解集和整数解族保持完整。
Result< ApproxReal > evaluate_numeric(const SymbolicExpr &expression, const NumericBindings &bindings={})
MixedTranscendentalResult solve_mixed_transcendental_checked(const std::shared_ptr< SymbolicExpr > &expr, const std::string &var, const SolveOptions &opts, ComputationContext &context)
int max_newton_iterations
Newton 迭代最大次数
NumericRootsResult solve_numeric_checked(const std::shared_ptr< SymbolicExpr > &expr, const std::string &var, const SolveOptions &opts)
bool contains_transcendental_of_var(const std::shared_ptr< SymbolicExpr > &expr, const std::string &var)
detail::SymbolicNodePtr substitute_free(const detail::SymbolicNodePtr &expression, const std::string &variable, const detail::SymbolicNodePtr &replacement, detail::RewriteBudget *budget)
方程求解选项,控制数值求解行为和结果形式.
数值求根:Newton-Raphson 迭代,二分法,Sturm 实根隔离.
多项式转换与精确符号多项式代数。
混合超越方程求解:符号预处理 + 自适应根隔离 + Newton-Raphson 精化.
超越方程求解:三角、指数、对数方程的符号反演。