LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
integrator.cpp
浏览该文件的文档.
3
4namespace LMCAS {
5namespace {
6bool is_zero(const SymbolicExpr& expression) {
7 const auto value = detail::exact_rational_value(detail::node(expression));
8 return value && value->is_zero();
9}
10}
11
12namespace detail {
14 const IntegralParts& parts, const std::string& variable, RewriteBudget& budget) {
15 const auto* primitive = dynamic_cast<const PiecewiseNode*>(node(parts.primitive).get());
16 const auto* remainder = dynamic_cast<const PiecewiseNode*>(node(parts.remainder).get());
17 if (primitive && remainder) {
18 std::vector<PiecewiseNode::Branch> branches;
19 branches.reserve(primitive->branches().size());
20 for (std::size_t i = 0; i < primitive->branches().size(); ++i) {
22 auto branch = materialize_integral({
23 expression_from_node(primitive->branches()[i].expression),
24 expression_from_node(remainder->branches()[i].expression)}, variable, budget);
25 branches.push_back({node(branch), primitive->branches()[i].condition});
26 }
27 SymbolicNodePtr fallback;
28 if (primitive->default_expr()) {
29 fallback = node(materialize_integral({expression_from_node(primitive->default_expr()),
30 expression_from_node(remainder->default_expr())}, variable, budget));
31 }
32 auto result = make_node<PiecewiseNode>(std::move(branches), fallback);
33 budget.measure(result);
34 return expression_from_node(result);
35 }
36 if (is_zero(parts.remainder)) { return parts.primitive; }
37 auto integral = make_node<IntegralNode>(node(parts.remainder), variable);
38 if (is_zero(parts.primitive)) { return expression_from_node(integral); }
40 {node(parts.primitive), integral}, &budget));
41}
42}
43
44Integrator::Integrator() : entries_(detail::make_integration_entries()),
45 zero_(*SymbolicExpr::number(0)) {}
46Integrator::~Integrator() = default;
47Integrator::Integrator(Integrator&&) noexcept = default;
48Integrator& Integrator::operator=(Integrator&&) noexcept = default;
49
50
51std::shared_ptr<SymbolicExpr> Integrator::check_cycle(
52 const SymbolicExpr& expr, const std::string& var, ComputationContext& context) {
53 detail::RewriteBudget budget(context, context.limits().max_recursion_depth,
54 context.limits().max_ast_nodes, "integrate.cycle");
55 for (const auto& entry : cycle_state_.history) {
57 if (detail::node(expr)->equals(*detail::node(entry.integrand))) {
58 return SymbolicExpr::variable(entry.placeholder);
59 }
61 detail::node(SymbolicExpr::divide(detail::make_expression_ptr(expr),
62 detail::make_expression_ptr(entry.integrand))), budget));
63 if (!depends_on_integration_variable(*ratio, var)) {
64 return SymbolicExpr::multiply(ratio, SymbolicExpr::variable(entry.placeholder));
65 }
66 }
67 return nullptr;
68}
69
70bool Integrator::has_cycle_placeholder(const SymbolicExpr& expression) const {
71 for (const auto& entry : cycle_state_.history) {
72 if (depends_on_integration_variable(expression, entry.placeholder)) { return true; }
73 }
74 return false;
75}
76
77bool Integrator::resolve_cycle(detail::IntegralParts& result,
78 const std::string& placeholder, const std::string& var, ComputationContext& context) {
79 if (depends_on_integration_variable(result.remainder, placeholder)) { return false; }
80 if (!depends_on_integration_variable(result.primitive, placeholder)) { return true; }
81 detail::RewriteBudget budget(context, context.limits().max_recursion_depth,
82 context.limits().max_ast_nodes, "integrate.cycle");
84 detail::node(result.primitive), placeholder, budget), budget);
85 if (expression_depends_on_variable(B, placeholder) ||
86 expression_depends_on_variable(B, var)) { return false; }
87 auto A = detail::substitute_node(detail::node(result.primitive), placeholder,
88 detail::node(zero_), budget);
90 *SymbolicExpr::number(1), detail::expression_from_node(B))), budget);
92 bool invertible = exact && !exact->is_zero();
93 if (!exact && context.assumptions()) {
95 auto nonzero = facts.is_nonzero(denominator, context);
96 if (!nonzero) { throw nonzero.error(); }
97 invertible = nonzero.value() == Tribool::True;
98 }
99 if (!invertible) { return false; }
102 SymbolicExpr::divide(detail::make_expression_ptr(A), divisor)), budget));
104 SymbolicExpr::divide(detail::make_expression_ptr(result.remainder), divisor)), budget));
105 return !depends_on_integration_variable(result.primitive, placeholder) &&
106 !depends_on_integration_variable(result.remainder, placeholder);
107}
108
109Result<std::optional<detail::IntegralParts>> Integrator::dispatch_builtins(
110 const SymbolicExpr& expr, const std::string& var, ComputationContext& context, int depth) {
112 using Method = Attempt (Integrator::*)(const SymbolicExpr&, const std::string&,
113 ComputationContext&, int);
114 constexpr Method methods[] = {
115 &Integrator::try_power_rule, &Integrator::try_table_lookup,
116 &Integrator::try_linear_substitution, &Integrator::try_substitution,
117 &Integrator::try_trig_substitution, &Integrator::try_trig_combination,
118 &Integrator::try_weierstrass, &Integrator::try_rational_decomposition,
119 &Integrator::try_special_function, &Integrator::try_integration_by_parts};
120 detail::RewriteBudget budget(context, context.limits().max_recursion_depth,
121 context.limits().max_ast_nodes, "integrate.builtins");
122 const auto placeholder = cycle_state_.history.back().placeholder;
123 std::optional<detail::IntegralParts> partial;
124 for (auto method : methods) {
125 auto access = context.consume_steps(1, "integrate.builtins");
126 if (!access) { return Attempt::failure(access.error()); }
127 auto attempted = (this->*method)(expr, var, context, depth);
128 if (!attempted) { return attempted; }
129 access = context.consume_steps(0, "integrate.builtins");
130 if (!access) { return Attempt::failure(access.error()); }
131 if (!attempted.value()) { continue; }
132 auto candidate = std::move(*attempted.value());
133 if (!resolve_cycle(candidate, placeholder, var, context)) { continue; }
135 detail::node(candidate.primitive), budget));
137 detail::node(candidate.remainder), budget));
138 if (is_zero(candidate.remainder)) { return Attempt::success(std::move(candidate)); }
139 if (!partial && (!is_zero(candidate.primitive) ||
140 !detail::node(candidate.remainder)->equals(*detail::node(expr)))) {
141 partial = std::move(candidate);
142 }
143 }
144 return Attempt::success(std::move(partial));
145}
146
147Result<detail::IntegralParts> Integrator::integrate_recursive(
148 const SymbolicExpr& source, const std::string& var,
149 ComputationContext& context, int depth, const SymbolicExpr* original) {
150 using PartsResult = Result<detail::IntegralParts>;
151 auto entered = context.enter_recursion("integrate.recursive");
152 if (!entered) { return PartsResult::failure(entered.error()); }
153 struct RecursionExit {
154 ComputationContext& context;
155 ~RecursionExit() { context.leave_recursion(); }
156 } recursion_exit{context};
157 detail::RewriteBudget budget(context, context.limits().max_recursion_depth,
158 context.limits().max_ast_nodes, "integrate.recursive");
159 budget.measure(detail::node(source));
160 const auto& integrand = original ? *original : source;
161 if (const auto* piecewise = dynamic_cast<const PiecewiseNode*>(detail::node(source).get())) {
162 std::vector<PiecewiseNode::Branch> primitives, remainders;
163 for (const auto& branch : piecewise->branches()) {
164 auto term = detail::expression_from_node(branch.expression);
165 auto parts = integrate_recursive(term, var, context, depth + 1);
166 if (!parts) { return parts; }
167 primitives.push_back({detail::node(parts.value().primitive), branch.condition});
168 remainders.push_back({detail::node(parts.value().remainder), branch.condition});
169 }
170 detail::SymbolicNodePtr primitive_default, remainder_default;
171 if (piecewise->default_expr()) {
172 auto term = detail::expression_from_node(piecewise->default_expr());
173 auto parts = integrate_recursive(term, var, context, depth + 1);
174 if (!parts) { return parts; }
175 primitive_default = detail::node(parts.value().primitive);
176 remainder_default = detail::node(parts.value().remainder);
177 }
179 std::move(primitives), primitive_default)),
181 std::move(remainders), remainder_default))});
182 }
183 auto linear = apply_linearity(source, var, context, depth);
184 if (!linear) { return PartsResult::failure(linear.error()); }
185 if (linear.value()) { return PartsResult::success(std::move(*linear.value())); }
186 const auto& working = detail::node(source);
187 std::optional<detail::AssumptionFacts> facts;
188 if (context.assumptions()) { facts.emplace(*context.assumptions()); }
189 auto expr = detail::expression_from_node(detail::normalize_node(working, budget,
190 facts ? static_cast<const FactsQuery&>(*facts) : detail::no_facts()));
191 if (is_zero(expr)) { return PartsResult::success({zero_, zero_}); }
192 if (!depends_on_integration_variable(expr, var)) {
193 return PartsResult::success({*SymbolicExpr::multiply(
194 detail::make_expression_ptr(expr), SymbolicExpr::variable(var)), zero_});
195 }
196 auto cycle = check_cycle(expr, var, context);
197 if (cycle) { return PartsResult::success({*cycle, zero_}); }
198 std::string placeholder;
199 do {
200 budget.consume();
201 placeholder = "INT_CYCLE_" + std::to_string(cycle_state_.next_placeholder++);
202 } while (cycle_state_.occupied_names.count(placeholder));
203 cycle_state_.occupied_names.insert(placeholder);
204 cycle_state_.history.push_back({expr, integrand, placeholder});
205 struct CycleExit {
206 CycleState& state;
207 ~CycleExit() { state.history.pop_back(); }
208 } cycle_exit{cycle_state_};
209 auto result = dispatch_builtins(expr, var, context, depth);
210 if (!result) { return PartsResult::failure(result.error()); }
211 if (result.value()) {
212 auto parts = std::move(*result.value());
213 if (!depends_on_integration_variable(parts.primitive, placeholder) &&
214 !depends_on_integration_variable(parts.remainder, placeholder)) {
215 return PartsResult::success(std::move(parts));
216 }
217 }
218 return PartsResult::success({zero_, integrand});
219}
220
221Result<SymbolicExpr> Integrator::integrate_checked(
222 const SymbolicExpr& expr, const std::string& var_name, ComputationContext& context) try {
223 if (var_name.empty()) {
225 "integration variable must not be empty", "integrate");
226 }
227 auto access = context.consume_steps(1, "integrate");
228 if (!access) { return Result<SymbolicExpr>::failure(access.error()); }
229 detail::RewriteBudget budget(context, context.limits().max_recursion_depth,
230 context.limits().max_ast_nodes, "integrate");
232 const bool top_level = query_depth_ == 0;
233 ++query_depth_;
234 struct QueryExit {
235 Integrator& integrator;
236 ~QueryExit() {
237 if (--integrator.query_depth_ == 0) {
238 integrator.cycle_state_.history.clear();
239 integrator.cycle_state_.occupied_names.clear();
240 }
241 }
242 } query_exit{*this};
243 if (top_level) {
244 cycle_state_.history.clear();
245 cycle_state_.next_placeholder = 0;
246 cycle_state_.occupied_names = detail::all_variable_names(detail::node(expr), &budget);
247 cycle_state_.occupied_names.insert(var_name);
248 if (context.assumptions()) {
250 cycle_state_.occupied_names, budget);
251 }
252 }
253 auto recursive = integrate_recursive(expr, var_name, context, 0, &expr);
254 if (!recursive) { return Result<SymbolicExpr>::failure(recursive.error()); }
255 auto parts = std::move(recursive.value());
256 if (has_cycle_placeholder(parts.primitive) || has_cycle_placeholder(parts.remainder)) {
257 parts = {zero_, expr};
258 }
259 auto result = detail::materialize_integral(parts, var_name, budget);
260 budget.measure(detail::node(result));
261 access = context.consume_steps(0, "integrate");
262 if (!access) { return Result<SymbolicExpr>::failure(access.error()); }
263 return Result<SymbolicExpr>::success(std::move(result));
264} catch (const CasError& error) {
265 return Result<SymbolicExpr>::failure(error);
266} catch (const std::bad_alloc&) {
268 "integration allocation failed", "integrate");
269} catch (const std::length_error& error) {
270 return Result<SymbolicExpr>::failure(CasErrc::ResourceLimit, error.what(), "integrate");
271} catch (const std::exception& error) {
272 return Result<SymbolicExpr>::failure(CasErrc::InternalInvariant, error.what(), "integrate");
273}
274
275}
分段函数节点,表示条件分支表达式.
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(T value)
static std::shared_ptr< const SymbolicNode > create_add(std::vector< std::shared_ptr< const SymbolicNode > > ops, detail::RewriteBudget *budget=nullptr)
创建加法节点,自动扁平化嵌套加法并消除零项.
static void collect_names(const AssumptionContext &assumptions, std::set< std::string > &names, RewriteBudget &budget)
std::size_t measure(const std::shared_ptr< const SymbolicNode > &node)
bool is_zero(const SymbolicExpr &expression)
SymbolicNodePtr differentiate_node(const SymbolicNodePtr &node, const std::string &variable, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
std::shared_ptr<::LMCAS::SymbolicExpr > make_expression_ptr(SymbolicNodePtr root)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
const std::string & variable
std::shared_ptr< const SymbolicNode > SymbolicNodePtr
const FactsQuery & no_facts() noexcept
inline ::LMCAS::SymbolicExpr expression_from_node(SymbolicNodePtr root)
RewriteBudget const FactsQuery & facts
SymbolicExpr materialize_integral(const IntegralParts &parts, const std::string &variable, RewriteBudget &budget)
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)
std::set< std::string > all_variable_names(const SymbolicNodePtr &expression, RewriteBudget *budget=nullptr)
SymbolicNodePtr substitute_node(const SymbolicNodePtr &node, const std::string &variable, const SymbolicNodePtr &replacement, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
bool expression_depends_on_variable(const detail::SymbolicNodePtr &expression, const std::string &variable, detail::RewriteBudget *budget)
@ True
Property definitely holds
bool depends_on_integration_variable(const SymbolicExpr &expression, const std::string &variable)
std::size_t std::size_t depth
const std::string const ExprPtr & value
std::shared_ptr< SymbolicExpr > sym_sub(const SymbolicExpr &lhs, const SymbolicExpr &rhs)
const ExprPtr & denominator
const std::vector< IntegrationStep > Integrator & integrator