LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
transcendental_inversion.cpp
浏览该文件的文档.
1#include <stdexcept>
6#include "poly_utils.hpp"
12#include "lmmc/config.h"
13#include <cmath>
14#include <vector>
16
18
19
21 const std::shared_ptr<SymbolicExpr>& expr,
22 lmmc_real_t& out, ComputationContext& context) {
23 if (!expr) return false;
24 auto numeric = detail::try_finite_numeric(*expr, context);
25 if (!numeric) {
26 return false;
27 }
28 out = static_cast<lmmc_real_t>(*numeric);
29 return true;
30}
31
32std::vector<std::shared_ptr<SymbolicExpr>> invert_lambert_w(
33 const std::shared_ptr<SymbolicExpr>& c, ComputationContext& context) {
34
35 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
36 (context).limits().max_ast_nodes, "transform");
37
38 auto sol = SymbolicExpr::lambertw(c);
39 if (!sol) {
40 return {};
41 }
42 return {detail::make_expression_ptr(detail::normalize_node(detail::node(sol), transform_budget))};
43}
44
45std::vector<std::shared_ptr<SymbolicExpr>> invert_exp_base(
46 const std::shared_ptr<SymbolicExpr>& base,
47 const std::shared_ptr<SymbolicExpr>& c, ComputationContext& context) {
48 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
49 (context).limits().max_ast_nodes, "transform");
50
51 lmmc_real_t c_val;
52 if (try_evaluate_numeric(c, c_val, context)) {
53 if (c_val <= 0.0) {
54 return {};
55 }
56 }
57
58 auto ln_c = SymbolicExpr::ln(c);
59 auto ln_a = SymbolicExpr::ln(base);
60 auto sol = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::divide(ln_c, ln_a)), transform_budget));
61 return {sol};
62}
63
64
65SolutionSet conditional_preimage(const ExprPtr& original, const std::string& var) {
66 auto relation = std::dynamic_pointer_cast<const RelationalNode>(detail::node(original));
67 auto predicate = relation ? original : SymbolicExpr::eq(original, SymbolicExpr::number(0));
68 return SolutionSet{ConditionalSolutions{ConditionSet{var, std::move(predicate), {}}}};
69}
70
71namespace {
72
73ExprPtr subtract(const ExprPtr& left, const ExprPtr& right) {
74 return SymbolicExpr::add(left, SymbolicExpr::multiply(SymbolicExpr::number(-1), right));
75}
76
79 type, std::vector<SymbolicNodePtr>{detail::node(target)}));
80}
81
83 const ExprPtr& inner, const std::string& var, ComputationContext& context) {
84 auto affine = detail::recognize_affine(*inner, var, context);
85 if (!affine || !affine.value()) { return affine; }
86 /**
87 * @brief 查询值前先规范化尚未求值的多项式系数。
88 * a 的实数性未知时,偏移量 0*a 仍为零;已声明为零的斜率须在除法前识别。
89 */
90 for (auto* coefficient : {&affine.value()->slope, &affine.value()->offset}) {
91 auto normalized = detail::simplify_expression(*coefficient, context);
92 if (!normalized) { return Result<std::optional<detail::AffineForm>>::failure(normalized.error()); }
93 *coefficient = std::move(normalized.value());
94 }
95 return affine;
96}
97
100 for (const auto& coefficient : {affine.slope, affine.offset}) {
101 if (coefficient == affine.slope && nonzero == Tribool::False) {
102 continue; /**< 已证有定义的零系数属于实数。 */
103 }
104 auto real_coefficient = detail::query_real_value(detail::node(coefficient), facts, context);
105 if (!real_coefficient) { return Result<bool>::failure(real_coefficient.error()); }
106 auto defined = detail::query_definedness(detail::node(coefficient), facts, Domain::Real, context);
107 if (!defined) { return Result<bool>::failure(defined.error()); }
108 if (real_coefficient.value() != Tribool::True || defined.value() != Tribool::True) {
109 return false;
110 }
111 }
112 return true;
113}
114
116 const ExprPtr& rhs, const ExprPtr& original, const std::string& var,
117 const FactsQuery& facts, ComputationContext& context, std::vector<ExprPtr>& conditions) {
119 if (!real) { return SolveResult::failure(real.error()); }
121 if (type != FunctionNode::FuncType::Tan) {
122 /**
123 * @brief 构造分支条件前先消除精确端点等式。
124 * 统一 +/-1 分支的恒真条件,使其可合并为完整的半周期解族。
125 */
126 auto lower_difference = detail::simplify_expression(SymbolicExpr::add(rhs, SymbolicExpr::number(1)), context);
127 if (!lower_difference) { return SolveResult::failure(lower_difference.error()); }
128 auto lower = detail::query_nonnegative_value(detail::node(lower_difference.value()), facts, context);
129 if (!lower) { return SolveResult::failure(lower.error()); }
130 auto upper_difference = detail::simplify_expression(subtract(SymbolicExpr::number(1), rhs), context);
131 if (!upper_difference) { return SolveResult::failure(upper_difference.error()); }
132 auto upper = detail::query_nonnegative_value(detail::node(upper_difference.value()), facts, context);
133 if (!upper) { return SolveResult::failure(upper.error()); }
134 if (lower.value() == Tribool::False || upper.value() == Tribool::False) {
136 }
137 if (lower.value() != Tribool::True) {
139 detail::node(rhs), detail::node(SymbolicExpr::number(-1)), RelationalNode::Op::GEQ)));
140 }
141 if (upper.value() != Tribool::True) {
143 detail::node(rhs), detail::node(SymbolicExpr::number(1)), RelationalNode::Op::LEQ)));
144 }
145 } else if (real.value() != Tribool::True) {
146 return SolveResult{conditional_preimage(original, var)};
147 }
148 return std::nullopt;
149}
150
152 const ExprPtr& offset, const ExprPtr& rhs, const ExprPtr& original, const std::string& var,
155 type, std::vector<SymbolicNodePtr>{detail::node(offset)}));
156 auto residual = detail::simplify_expression(subtract(constant, rhs), context);
157 if (!residual) { return SolveResult::failure(residual.error()); }
158 auto unequal = detail::query_nonzero_value(detail::node(residual.value()), facts, Domain::Real, context);
159 if (!unequal) { return SolveResult::failure(unequal.error()); }
160 if (unequal.value() == Tribool::False) { return SolutionSet{UniversalSolutions{}}; }
161 if (unequal.value() == Tribool::True) { return SolutionSet{EmptySolutions{}}; }
162 return conditional_preimage(original, var);
163}
164
166 const std::string& var, ComputationContext& context) {
167 detail::RewriteBudget budget(context, context.limits().max_recursion_depth,
168 context.limits().max_ast_nodes, "solve.parameter");
169 auto occupied = detail::all_variable_names(detail::node(original), &budget);
170 occupied.insert(var);
171 if (context.assumptions()) {
172 detail::AssumptionFacts::collect_names(*context.assumptions(), occupied, budget);
173 }
174 std::string parameter = "_k";
175 for (std::size_t suffix = 1; occupied.count(parameter); ++suffix) {
176 budget.consume();
177 parameter = "_k" + std::to_string(suffix);
178 }
179 return parameter;
180}
181
183 const ExprPtr& rhs, const ExprPtr& pi) {
184 using Type = FunctionNode::FuncType;
185 auto principal = inverse_trig(type == Type::Sin ? Type::ArcSin : Type::ArcCos, rhs);
186 const auto positive_half = SymbolicExpr::number(Rational(1, 2));
187 const auto negative_half = SymbolicExpr::number(Rational(-1, 2));
188 if (detail::node(rhs)->equals(*detail::node(positive_half))) {
189 principal = SymbolicExpr::multiply(SymbolicExpr::number(
190 type == Type::Sin ? Rational(1, 6) : Rational(1, 3)), pi);
191 } else if (detail::node(rhs)->equals(*detail::node(negative_half))) {
192 principal = SymbolicExpr::multiply(SymbolicExpr::number(
193 type == Type::Sin ? Rational(-1, 6) : Rational(2, 3)), pi);
194 }
195 return principal;
196}
197
199 const ExprPtr& rhs, const std::string& parameter, ComputationContext& context) {
200
201 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
202 (context).limits().max_ast_nodes, "transform");
203 using Type = FunctionNode::FuncType;
206 auto integer_pi = SymbolicExpr::multiply(SymbolicExpr::variable(parameter), pi);
207 auto twice_integer_pi = SymbolicExpr::multiply(SymbolicExpr::number(2), integer_pi);
208 auto half_pi = SymbolicExpr::multiply(SymbolicExpr::number(Rational(1, 2)), pi);
210 std::vector<ExprPtr> values;
211 if (type == Type::Tan) {
212 auto principal = integer ?
213 SymbolicExpr::multiply(SymbolicExpr::number(Rational(*integer, 4)), pi) :
214 inverse_trig(Type::ArcTan, rhs);
215 values.push_back(SymbolicExpr::add(principal, integer_pi));
216 } else if (integer && *integer == 0) {
217 values.push_back(type == Type::Sin ? integer_pi :
218 SymbolicExpr::add(half_pi, integer_pi));
219 } else if (integer && (*integer == 1 || *integer == -1)) {
220 auto principal = type == Type::Sin ?
221 SymbolicExpr::multiply(SymbolicExpr::number(*integer), half_pi) :
222 (*integer == 1 ? SymbolicExpr::number(0) : pi);
223 values.push_back(SymbolicExpr::add(principal, twice_integer_pi));
224 } else {
225 auto principal = trigonometric_principal_value(type, rhs, pi);
226 values.push_back(SymbolicExpr::add(principal, twice_integer_pi));
227 values.push_back(SymbolicExpr::add(
228 type == Type::Sin ? subtract(pi, principal) :
229 SymbolicExpr::multiply(SymbolicExpr::number(-1), principal),
230 twice_integer_pi));
231 }
232 return values;
233}
234
236 const InversePattern& pattern, const ExprPtr& rhs, const ExprPtr& original,
237 const std::string& var, const FactsQuery& facts, ComputationContext& context) {
238 auto affine = prepare_trigonometric_affine(pattern.inner, var, context);
239 if (!affine) { return SolveResult::failure(affine.error()); }
240 if (!affine.value()) { return conditional_preimage(original, var); }
241 auto nonzero = detail::query_nonzero_value(detail::node(affine.value()->slope), facts, Domain::Real, context);
242 if (!nonzero) { return SolveResult::failure(nonzero.error()); }
243 auto real_coefficients = real_affine_coefficients(*affine.value(), nonzero.value(), facts, context);
244 if (!real_coefficients) { return SolveResult::failure(real_coefficients.error()); }
245 if (!real_coefficients.value()) { return conditional_preimage(original, var); }
246 std::vector<ExprPtr> conditions;
247 if (auto decided = trigonometric_target_range(
248 pattern.func_type, rhs, original, var, facts, context, conditions)) {
249 return std::move(*decided);
250 }
251 if (nonzero.value() == Tribool::False) {
253 pattern.func_type, affine.value()->offset, rhs, original, var, facts, context);
254 }
255 if (nonzero.value() != Tribool::True) { return conditional_preimage(original, var); }
256 auto fresh = capture_free_integer_parameter(original, var, context);
257 if (!fresh) { return SolveResult::failure(fresh.error()); }
258 const auto& parameter = fresh.value();
259 auto values = trigonometric_periodic_values(pattern.func_type, rhs, parameter, context);
260 ParametricSolutions solutions;
261 for (const auto& value : values) {
262 auto mapped = detail::simplify_expression(SymbolicExpr::divide(
263 subtract(value, affine.value()->offset), affine.value()->slope), context);
264 if (!mapped) { return SolveResult::failure(mapped.error()); }
265 solutions.values.push_back(ParametricSolution{
266 std::move(mapped.value()), {parameter}, conditions});
267 }
268 return SolutionSet{std::move(solutions)};
269}
270
272 const std::string& var, const FactsQuery& facts, ComputationContext& context) try {
273 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
274 (context).limits().max_ast_nodes, "transform");
276 if (!real) { return SolveResult::failure(real.error()); }
278 if (!positive) { return SolveResult::failure(positive.error()); }
279 if (real.value() == Tribool::True && positive.value() == Tribool::False) {
280 return SolutionSet{EmptySolutions{}};
281 }
282 if (positive.value() != Tribool::True) { return conditional_preimage(original, var); }
283 auto value = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::ln(rhs)), transform_budget));
284 return SolutionSet{FiniteSolutions{{FiniteSolution{std::move(value), 1, {}}}}};
285} catch (const CasError& error) {
286 return SolveResult::failure(error);
287} catch (const std::bad_alloc&) {
288 return SolveResult::failure(CasErrc::ResourceLimit, "allocation failed", "invert_exponential");
289} catch (const std::length_error& error) {
290 return SolveResult::failure(CasErrc::ResourceLimit, error.what(), "invert_exponential");
291} catch (const std::exception& error) {
292 return SolveResult::failure(CasErrc::InternalInvariant, error.what(), "invert_exponential");
293}
294
296 const std::string& var, const FactsQuery& facts, ComputationContext& context) try {
297 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
298 (context).limits().max_ast_nodes, "transform");
300 if (!real) { return SolveResult::failure(real.error()); }
301 if (real.value() != Tribool::True) { return conditional_preimage(original, var); }
302 auto value = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::exp(rhs)), transform_budget));
303 return SolutionSet{FiniteSolutions{{FiniteSolution{std::move(value), 1, {}}}}};
304} catch (const CasError& error) {
305 return SolveResult::failure(error);
306} catch (const std::bad_alloc&) {
307 return SolveResult::failure(CasErrc::ResourceLimit, "allocation failed", "invert_logarithm");
308} catch (const std::length_error& error) {
309 return SolveResult::failure(CasErrc::ResourceLimit, error.what(), "invert_logarithm");
310} catch (const std::exception& error) {
311 return SolveResult::failure(CasErrc::InternalInvariant, error.what(), "invert_logarithm");
312}
313
314}
315
317 const InversePattern& pattern, const ExprPtr& original, const std::string& var,
319 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
320 (context).limits().max_ast_nodes, "transform");
321 auto step = context.consume_steps(1, "solve.inverse");
322 if (!step) { return SolveResult::failure(step.error()); }
323 std::optional<detail::AssumptionFacts> assumption_facts;
324 if (context.assumptions()) { assumption_facts.emplace(*context.assumptions()); }
325 const FactsQuery& facts = assumption_facts ? static_cast<const FactsQuery&>(*assumption_facts) :
327 auto rhs = pattern.rhs;
328 if (pattern.coefficient) {
330 if (!nonzero) { return SolveResult::failure(nonzero.error()); }
331 if (nonzero.value() != Tribool::True) { return conditional_preimage(original, var); }
332 rhs = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::divide(rhs, pattern.coefficient)), transform_budget));
333 }
334 switch (pattern.func_type) {
338 return invert_trigonometric(pattern, rhs, original, var, facts, context);
340 return invert_exponential(rhs, original, var, facts, context);
342 return invert_logarithm(rhs, original, var, facts, context);
343 default:
345 "Inverse requires a complete solution-set representation", "solve.inverse");
346 }
347} catch (const CasError& error) {
348 return SolveResult::failure(error);
349} catch (const std::bad_alloc&) {
350 return SolveResult::failure(CasErrc::ResourceLimit, "allocation failed", "invert_function");
351} catch (const std::length_error& error) {
352 return SolveResult::failure(CasErrc::ResourceLimit, error.what(), "invert_function");
353} catch (const std::exception& error) {
354 return SolveResult::failure(CasErrc::InternalInvariant, error.what(), "invert_function");
355}
356
357
358}
AssumptionContext class with scoped push/pop management.
CasError & error()
static Result failure(CasErrc code, std::string message, std::string operation={})
static void collect_names(const AssumptionContext &assumptions, std::set< std::string > &names, RewriteBudget &budget)
Result< std::optional< detail::AffineForm > > prepare_trigonometric_affine(const ExprPtr &inner, const std::string &var, ComputationContext &context)
SolveResult invert_trigonometric(const InversePattern &pattern, const ExprPtr &rhs, const ExprPtr &original, const std::string &var, const FactsQuery &facts, ComputationContext &context)
SolveResult invert_exponential(const ExprPtr &rhs, const ExprPtr &original, const std::string &var, const FactsQuery &facts, ComputationContext &context)
std::optional< SolveResult > trigonometric_target_range(FunctionNode::FuncType type, const ExprPtr &rhs, const ExprPtr &original, const std::string &var, const FactsQuery &facts, ComputationContext &context, std::vector< ExprPtr > &conditions)
Result< std::string > capture_free_integer_parameter(const ExprPtr &original, const std::string &var, ComputationContext &context)
SolveResult invert_logarithm(const ExprPtr &rhs, const ExprPtr &original, const std::string &var, const FactsQuery &facts, ComputationContext &context)
std::vector< ExprPtr > trigonometric_periodic_values(FunctionNode::FuncType type, const ExprPtr &rhs, const std::string &parameter, ComputationContext &context)
Result< bool > real_affine_coefficients(const detail::AffineForm &affine, Tribool nonzero, const FactsQuery &facts, ComputationContext &context)
ExprPtr trigonometric_principal_value(FunctionNode::FuncType type, const ExprPtr &rhs, const ExprPtr &pi)
SolveResult constant_trigonometric_preimage(FunctionNode::FuncType type, const ExprPtr &offset, const ExprPtr &rhs, const ExprPtr &original, const std::string &var, const FactsQuery &facts, 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)
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)
bool try_evaluate_numeric(const std::shared_ptr< SymbolicExpr > &expr, lmmc_real_t &out, 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)
std::shared_ptr< SymbolicExpr > slope
ExactRealAlgebraic rhs
Result< Tribool > query_nonzero_value(const Node &node, const FactsQuery &facts, Domain domain, ComputationContext &context)
const FactsQuery & no_facts() noexcept
std::optional< double > try_finite_numeric(const SymbolicExpr &expression, ComputationContext &context)
Result< Tribool > query_definedness(const Node &node, const FactsQuery &facts, Domain domain, ComputationContext &context, bool *violated_real_restriction)
RewriteBudget const FactsQuery & facts
std::shared_ptr< const Node > make_node(Args &&... args)
Result< Tribool > query_real_value(const Node &node, const FactsQuery &facts, ComputationContext &context)
SymbolicNodePtr normalize_node(const SymbolicNodePtr &node, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
Result< Tribool > query_nonnegative_value(const Node &node, const FactsQuery &facts, ComputationContext &context)
std::size_t std::size_t ComputationContext & context
std::set< std::string > all_variable_names(const SymbolicNodePtr &expression, RewriteBudget *budget=nullptr)
Result< std::optional< AffineForm > > recognize_affine(const SymbolicExpr &expression, const std::string &variable, ComputationContext &context)
Result< Tribool > query_positive_value(const Node &node, const FactsQuery &facts, ComputationContext &context)
std::shared_ptr< SymbolicExpr > offset
__attribute__((visibility("default"))) ExprResult function(const std __attribute__((visibility("default"))) ExprResult finite_set(std ExprPtr upper
ExprResult integer(Integer)=delete
ExprResult real(const ExprPtr &expression, ComputationContext &context)
Result< SolutionSet > SolveResult
const ExprPtr & lower
@ Real
Real numbers
const VectorField const VectorField & c
Tribool
Tri-state logic value for property queries.
@ True
Property definitely holds
@ False
Property definitely does not hold
std::optional< int > exact_small_integer_node(const detail::SymbolicNodePtr &node, int min_value, int max_value)
std::shared_ptr< SymbolicExpr > ExprPtr
const ExprPtr & target
const ExprPtr & right
ExprResult pi()
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 是在操作上下文假设下须同时满足的条件。 空条件保留原表达式定义域且不增加约束;整数参数遍历全体整数。
方程求解选项,控制数值求解行为和结果形式.
多项式转换与精确符号多项式代数。