25 const ExprPtr& original,
const ExprPtr& expr,
const std::string& var,
33 (
context).limits().max_ast_nodes,
"transform");
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) {
51 SymbolicExpr::number(-1),
value)), transform_budget));
56 auto equation = SymbolicExpr::add(inner,
57 SymbolicExpr::multiply(
value, SymbolicExpr::number(-1)));
58 auto polynomial = symbolic_to_poly<SymbolicPolyCoeff>(equation, var);
66}
catch (
const std::bad_alloc&) {
68}
catch (
const std::length_error& error) {
70}
catch (
const std::exception& error) {
75 for (
auto& candidate : finite.
values) {
76 auto duplicate = std::find_if(prior.
values.begin(), prior.
values.end(),
78 return candidate.conditions.empty() && previous.conditions.empty() &&
79 detail::node(candidate.value)->equals(*detail::node(previous.value));
81 if (duplicate == prior.
values.end()) { prior.
values.push_back(std::move(candidate)); }
90 promoted.
values.push_back({std::move(
point.value), {}, std::move(
point.conditions)});
92 output = std::move(promoted);
94 auto& result = std::get<ParametricSolutions>(output).values;
97 result.push_back({std::move(
point.value), {}, std::move(
point.conditions)});
100 for (
auto& family : families->
values) { result.push_back(std::move(family)); }
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);
112 auto* finite = std::get_if<FiniteSolutions>(&solved);
113 auto* prior = std::get_if<FiniteSolutions>(&output);
114 if (finite && prior) {
118 auto* families = std::get_if<ParametricSolutions>(&solved);
119 if ((finite || families) && (prior || std::holds_alternative<ParametricSolutions>(output))) {
128 SymbolicExpr::multiply(SymbolicExpr::number(-1),
right)),
context);
132 return std::holds_alternative<ProvedZeroResidual>(proof.value());
136 if (
right.integer_parameters.size() != 1 || left.
conditions.size() !=
right.conditions.size()) {
143 right.integer_parameters.front()) ||
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);
162 if (!forward) {
return MergeResult::failure(forward.error()); }
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>{}; }
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())};
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}) {
186 *coefficient = std::move(normalized.value());
190 if (!equal_slopes.value()) {
return false; }
194 if (!merged.value()) {
return false; }
195 left.
value = std::move(*merged.value());
200 auto* parametric = std::get_if<ParametricSolutions>(&solutions);
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();) {
208 if (!merged.value()) { ++j;
continue; }
209 families.erase(families.begin() + j);
218 const std::vector<ExprPtr>& values,
const std::string& var,
220 if (values.empty())
return inconclusive(
"Inverse did not establish a complete value set");
222 for (
const auto&
value : values) {
224 if (!solved)
return solved;
233 auto polynomial = symbolic_to_poly<SymbolicPolyCoeff>(
237 return inconclusive(
"Substitution is not a supported polynomial");
242 if (!solved)
return solved;
243 if (std::holds_alternative<EmptySolutions>(solved.value()))
return solved;
244 auto finite = std::get_if<FiniteSolutions>(&solved.value());
247 for (
const auto&
value : finite->values) {
251 if (!inverted)
return inverted;
260 (
context).limits().max_ast_nodes,
"transform");
261 std::optional<detail::AssumptionFacts> assumptions;
262 if (
context.assumptions()) { assumptions.emplace(*
context.assumptions()); }
272}
catch (
const std::bad_alloc&) {
274}
catch (
const std::length_error& error) {
276}
catch (
const std::exception& error) {
283 if (!inverted) {
return inverted; }
284 auto finite = std::get_if<FiniteSolutions>(&inverted.value());
285 if (!finite) {
return inverted; }
287 for (
const auto&
value : finite->values) {
291 if (!solved) {
return solved; }
298 const ExprPtr& original,
const ExprPtr& expr,
const std::string& var,
300 auto step =
context.consume_steps(1,
"solve.transcendental");
321 if (substitution.value()) {
324 return inconclusive(
"No complete transcendental inversion matched");
330 const ExprPtr& expr,
const std::string& var,
334 "Expression and variable must not be empty",
"solve.transcendental");
337 context.limits().max_ast_nodes,
338 "solve.transcendental");
339 auto equation = expr;
341 if (
relation->op() != RelationalNode::Op::EQ) {
343 "Expected an equality",
"solve.transcendental");
346 SymbolicExpr::multiply(SymbolicExpr::number(-1),
350 auto solved = std::dynamic_pointer_cast<const MultiplyNode>(
detail::node(equation)) ?
352 solve_transcendental_impl(expr, equation, var,
context,
options, 0);
353 if (!solved)
return solved;
354 auto coalesced = coalesce_periodic_families(solved.value(),
context);
358catch (
const CasError& error) {
360}
catch (
const std::bad_alloc&) {
362 "transcendental solve allocation failed",
"solve.transcendental");
363}
catch (
const std::length_error& error) {
365}
catch (
const std::exception& error) {
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)
constexpr int maximum_transcendental_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 ¶meter, 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)
SolveResult inconclusive(const char *message)
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< SymbolicExpr > inner
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
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)
std::vector< ExprPtr > conditions
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 及相关选项结构体.