LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
symbolic_cancel.cpp
浏览该文件的文档.
2#include "symbolic.hpp"
5#include "poly_utils.hpp"
6
7#include <vector>
8
9namespace LMCAS {
10namespace {
11
12using CancellationNode = std::shared_ptr<const SymbolicNode>;
13using CancellationExpr = std::shared_ptr<SymbolicExpr>;
14
16 const auto& value = exponent.value();
17 if (std::holds_alternative<BigInt>(value)) {
18 const auto& integer = std::get<BigInt>(value);
19 if (!integer.is_negative()) { return nullptr; }
20 return detail::make_node<NumberNode>(BigInt(0) - integer);
21 }
22 const auto& rational = std::get<Rational>(value);
23 if (!rational.get_numerator().is_negative()) { return nullptr; }
24 return detail::make_node<NumberNode>(Rational(
25 BigInt(0) - rational.get_numerator(), rational.get_denominator()));
26}
27
29 std::vector<CancellationNode>& numerator, std::vector<CancellationNode>& denominator) {
30 auto power = std::dynamic_pointer_cast<const PowerNode>(factor);
31 if (power) {
32 auto exponent = std::dynamic_pointer_cast<const NumberNode>(power->exponent());
33 auto positive = exponent ? positive_denominator_exponent(*exponent) : nullptr;
34 if (positive) {
35 auto number = std::dynamic_pointer_cast<const NumberNode>(positive);
36 if (number && number->is_one()) {
37 denominator.push_back(power->base());
38 } else {
39 denominator.push_back(detail::make_node<PowerNode>(power->base(), positive));
40 }
41 return;
42 }
43 }
44 numerator.push_back(factor);
45}
46
47static void separate_num_den(const CancellationNode& node,
48 std::vector<CancellationNode>& numerator, std::vector<CancellationNode>& denominator) {
49 if (auto product = std::dynamic_pointer_cast<const MultiplyNode>(node)) {
50 for (const auto& operand : product->operands()) {
51 classify_fraction_factor(operand, numerator, denominator);
52 }
53 } else {
54 classify_fraction_factor(node, numerator, denominator);
55 }
56}
57
58static CancellationExpr build_product(const std::vector<CancellationNode>& factors) {
59 if (factors.empty()) { return SymbolicExpr::number(1); }
60 if (factors.size() == 1) { return detail::make_expression_ptr(factors[0]); }
62}
63
68
69static bool collect_fraction_terms(const AddNode& addition,
70 std::vector<CancellationFraction>& terms, ComputationContext& context) {
71
72 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
73 (context).limits().max_ast_nodes, "transform");
74 bool has_denominator = false;
75 for (const auto& term : addition.operands()) {
76 std::vector<CancellationNode> numerator, denominator;
77 separate_num_den(term, numerator, denominator);
78 auto num_expr = detail::make_expression_ptr(detail::normalize_node(detail::node(build_product(numerator)), transform_budget));
80 if (!den_expr->is_one()) { has_denominator = true; }
81 terms.push_back({num_expr, den_expr});
82 }
83 return has_denominator;
84}
85
86static bool same_denominators(const std::vector<CancellationFraction>& terms) {
87 auto first = terms[0].denominator->to_string();
88 for (size_t i = 1; i < terms.size(); ++i) {
89 if (terms[i].denominator->to_string() != first) { return false; }
90 }
91 return true;
92}
93
95 const std::vector<CancellationFraction>& terms, ComputationContext& context) {
96
97 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
98 (context).limits().max_ast_nodes, "transform");
99 auto total_den = SymbolicExpr::number(1);
100 std::vector<CancellationExpr> unique_dens;
101 for (const auto& term : terms) {
102 if (term.denominator->is_one()) { continue; }
103 bool found = false;
104 for (const auto& denominator : unique_dens) {
105 if (denominator->to_string() == term.denominator->to_string()) {
106 found = true;
107 break;
108 }
109 }
110 if (!found) { unique_dens.push_back(term.denominator); }
111 }
112 for (const auto& denominator : unique_dens) {
113 total_den = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::multiply(total_den, denominator)), transform_budget));
114 }
115 return total_den;
116}
117
119 const std::vector<CancellationFraction>& terms, ComputationContext& context) {
120
121 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
122 (context).limits().max_ast_nodes, "transform");
123 const bool same = same_denominators(terms);
124 auto denominator = same ? terms[0].denominator : distinct_denominator_product(terms, context);
125 std::vector<CancellationNode> operands;
126 for (const auto& term : terms) {
127 auto numerator = term.numerator;
128 if (!same) {
129 const auto& total_node = detail::node(denominator);
130 const auto& term_node = detail::node(term.denominator);
131 auto factor = total_node && term_node &&
132 total_node->compare(*term_node) == 0
133 ? SymbolicExpr::number(1)
135 denominator, term.denominator)), transform_budget));
136 numerator = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::multiply(
137 term.numerator, factor)), transform_budget));
138 }
139 if (numerator && detail::node(numerator)) {
140 operands.push_back(detail::node(numerator));
141 }
142 }
143 if (operands.empty()) { return {SymbolicExpr::number(0), nullptr}; }
144 auto numerator = operands.size() == 1 ? detail::make_expression_ptr(operands[0])
146 numerator = same ? detail::make_expression_ptr(detail::normalize_node(detail::node(numerator), transform_budget)) : detail::make_expression_ptr(detail::expand_node(detail::node(numerator), transform_budget));
147 return {numerator, denominator};
148}
149
150
151template <typename Coefficient>
155
156 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
157 (context).limits().max_ast_nodes, "transform");
158 if (denominator.is_zero()) return;
159 auto gcd = Polynomial<Coefficient>::gcd(numerator, denominator);
160 if (gcd.degree() >= 1) {
161 auto [q_num, r_num] = numerator.div_mod(gcd);
162 auto [q_den, r_den] = denominator.div_mod(gcd);
163 if (!r_num.is_zero() || !r_den.is_zero()) return;
164 cur_num = detail::make_expression_ptr(detail::normalize_node(detail::node(poly_to_symbolic(q_num)), transform_budget));
165 cur_den = detail::make_expression_ptr(detail::normalize_node(detail::node(poly_to_symbolic(q_den)), transform_budget));
166 }
167}
168
170 CancellationExpr& cur_den, const std::string& var, const CancellationExpr& simp, ComputationContext& context) {
171
172 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
173 (context).limits().max_ast_nodes, "transform");
174 auto num_expanded = detail::make_expression_ptr(detail::expand_node(detail::node(cur_num), transform_budget));
175 auto den_expanded = detail::make_expression_ptr(detail::expand_node(detail::node(cur_den), transform_budget));
176 auto rational_num = symbolic_to_poly<Rational>(num_expanded, var);
177 auto rational_den = symbolic_to_poly<Rational>(den_expanded, var);
178 if (rational_num && rational_den) {
179 if (rational_den.value().is_zero()) { return simp; }
180 if (rational_num.value().is_zero()) { return SymbolicExpr::number(0); }
181 cancel_polynomial_gcd(rational_num.value(), rational_den.value(), cur_num, cur_den, context);
182 return nullptr;
183 }
184 auto symbolic_num = symbolic_to_poly<SymbolicPolyCoeff>(num_expanded, var);
185 auto symbolic_den = symbolic_to_poly<SymbolicPolyCoeff>(den_expanded, var);
186 if (!symbolic_num || !symbolic_den) { return nullptr; }
187 if (symbolic_den.value().is_zero()) { return simp; }
188 if (symbolic_num.value().is_zero()) { return SymbolicExpr::number(0); }
189 cancel_polynomial_gcd(symbolic_num.value(), symbolic_den.value(), cur_num, cur_den, context);
190 return nullptr;
191}
192
195
196 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
197 (context).limits().max_ast_nodes, "transform");
198 if (cur_den->is_one()) { return cur_num; }
199 if (detail::node(cur_den)) {
201 SymbolicExpr::number(-1))), transform_budget));
202 if (diff->is_zero()) {
203 return detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::multiply(SymbolicExpr::number(-1), cur_num)), transform_budget));
204 }
205 }
206 return detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::divide(cur_num, cur_den)), transform_budget));
207}
208
210 const CancellationExpr& simp, bool from_sum, ComputationContext& context) {
211
212 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
213 (context).limits().max_ast_nodes, "transform");
214 auto rational_variables = free_variables(detail::node(fraction.numerator));
215 const auto denominator_variables = free_variables(detail::node(fraction.denominator));
216 rational_variables.insert(denominator_variables.begin(), denominator_variables.end());
217 if (!from_sum && rational_variables.empty()) {
218 return detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::divide(fraction.numerator, fraction.denominator)), transform_budget));
219 }
220 for (const auto& var : rational_variables) {
221 try {
222 auto terminal = cancel_variable(
223 fraction.numerator, fraction.denominator, var, simp, context);
224 if (terminal) { return terminal; }
225 } catch (const std::invalid_argument&) {
226 continue;
227 } catch (const std::out_of_range&) {
228 continue;
229 } catch (const std::runtime_error&) {
230 continue;
231 }
232 }
233 return finish_cancellation(fraction.numerator, fraction.denominator, context);
234}
235
236}
237
238std::shared_ptr<SymbolicExpr> SymbolicExpr::cancel() const {
239 ComputationContext context;
240 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
241 (context).limits().max_ast_nodes, "transform");
242 auto simp = detail::make_expression_ptr(
243 detail::normalize_node(detail::node(*this), transform_budget));
244 if (auto addition = std::dynamic_pointer_cast<const AddNode>(detail::node(simp))) {
245 std::vector<CancellationFraction> terms;
246 if (collect_fraction_terms(*addition, terms, context)) {
247 auto combined = combine_fraction_terms(terms, context);
248 if (!combined.denominator) { return combined.numerator; }
249 combined.denominator = detail::make_expression_ptr(detail::expand_node(detail::node(combined.denominator), transform_budget));
250 return reduce_fraction(std::move(combined), simp, true, context);
251 }
252 }
253 std::vector<CancellationNode> num_factors;
254 std::vector<CancellationNode> den_factors;
255 separate_num_den(detail::node(simp), num_factors, den_factors);
256 if (den_factors.empty()) { return simp; }
257 auto numerator = detail::make_expression_ptr(detail::normalize_node(detail::node(build_product(num_factors)), transform_budget));
259 if (denominator->is_one()) { return numerator; }
260 return reduce_fraction({numerator, denominator}, simp, false, context);
261}
262
263}
加法节点,表示多个操作数的求和。
const std::vector< std::shared_ptr< const SymbolicNode > > & operands() const noexcept
一元多项式模板类
static Polynomial gcd(Polynomial a, Polynomial b)
计算两个多项式的最大公因式
std::pair< Polynomial, Polynomial > div_mod(const Polynomial &other) const
多项式带余除法
int compare(const SymbolicNode &other) const
与另一个节点进行全序比较.
std::shared_ptr< const SymbolicNode > CancellationNode
static CancellationFraction combine_fraction_terms(const std::vector< CancellationFraction > &terms, ComputationContext &context)
static CancellationExpr reduce_fraction(CancellationFraction fraction, const CancellationExpr &simp, bool from_sum, ComputationContext &context)
static bool collect_fraction_terms(const AddNode &addition, std::vector< CancellationFraction > &terms, ComputationContext &context)
static bool same_denominators(const std::vector< CancellationFraction > &terms)
static CancellationExpr distinct_denominator_product(const std::vector< CancellationFraction > &terms, ComputationContext &context)
static CancellationNode positive_denominator_exponent(const NumberNode &exponent)
static CancellationExpr cancel_variable(CancellationExpr &cur_num, CancellationExpr &cur_den, const std::string &var, const CancellationExpr &simp, ComputationContext &context)
static void cancel_polynomial_gcd(const Polynomial< Coefficient > &numerator, const Polynomial< Coefficient > &denominator, CancellationExpr &cur_num, CancellationExpr &cur_den, ComputationContext &context)
static CancellationExpr build_product(const std::vector< CancellationNode > &factors)
static void separate_num_den(const CancellationNode &node, std::vector< CancellationNode > &numerator, std::vector< CancellationNode > &denominator)
static CancellationExpr finish_cancellation(const CancellationExpr &cur_num, const CancellationExpr &cur_den, ComputationContext &context)
static void classify_fraction_factor(const CancellationNode &factor, std::vector< CancellationNode > &numerator, std::vector< CancellationNode > &denominator)
SymbolicNodePtr expand_node(const SymbolicNodePtr &node, 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
std::shared_ptr< const Node > make_node(Args &&... args)
SymbolicNodePtr normalize_node(const SymbolicNodePtr &node, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
ExprResult integer(Integer)=delete
const ExprPtr & exponent
std::set< std::string > free_variables(const detail::SymbolicNodePtr &expression, detail::RewriteBudget *budget)
const std::string const ExprPtr & value
const ExprPtr & denominator
ExprResult rational(const Rational &value)
多项式转换与精确符号多项式代数。
符号表达式主类 SymbolicExpr 及其工厂方法,运算接口.
内部 AST 定义。