LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
expression_analysis.cpp
浏览该文件的文档.
2
3#include <unordered_map>
4#include <utility>
5
6namespace LMCAS {
7namespace {
8
10public:
11 using RecursiveSymbolicVisitor::RecursiveSymbolicVisitor;
12 std::set<std::string> variables;
13
14 void visit(const VariableNode& node) override {
15 if (!node.is_constant() && !is_bound(node.name())) variables.insert(node.name());
16 }
17
18 void visit(const SummationNode& node) override { visit_binder(node); }
19 void visit(const ProductNode& node) override { visit_binder(node); }
20 void visit(const IntegralNode& node) override { visit_binder(node); }
21 void visit(const TransformNode& node) override { visit_binder(node); }
22 void visit(const QuantifierNode& node) override { visit_binder(node); }
23 void visit(const SetBuilderNode& node) override { visit_binder(node); }
24 void visit(const LimitNode& node) override { visit_binder(node); }
25
26 void visit(const RootOfNode&) override {}
27
28private:
29 bool is_bound(const std::string& name) const {
30 const auto found = bound_.find(name);
31 return found != bound_.end() && found->second != 0;
32 }
33
34 void visit_binder(const SymbolicNode& node) {
35 const auto binder = detail::binder_view(node);
36 visit_children(binder->outside_scope);
37 ++bound_[binder->bound_name];
38 visit_child(binder->scoped_body);
39 auto found = bound_.find(binder->bound_name);
40 if (--found->second == 0) bound_.erase(found);
41 }
42
43 std::unordered_map<std::string_view, std::size_t> bound_;
44};
46public:
47 explicit FreeVariableFinder(std::string_view name, detail::RewriteBudget* budget = nullptr,
48 bool antiderivatives = false)
49 : RecursiveSymbolicVisitor(budget), name_(name), antiderivatives_(antiderivatives) {}
50 bool found = false;
51
52 void visit(const VariableNode& node) override {
53 found = found || (!node.is_constant() && node.name() == name_);
54 }
55 void visit(const SummationNode& node) override { visit_binder(node); }
56 void visit(const ProductNode& node) override { visit_binder(node); }
57 void visit(const IntegralNode& node) override {
58 if (antiderivatives_ && !node.lower() && !node.upper() && node.variable() == name_) {
59 found = true;
60 } else { visit_binder(node); }
61 }
62 void visit(const TransformNode& node) override { visit_binder(node); }
63 void visit(const QuantifierNode& node) override { visit_binder(node); }
64 void visit(const SetBuilderNode& node) override { visit_binder(node); }
65 void visit(const LimitNode& node) override { visit_binder(node); }
66
67private:
68 void visit_binder(const SymbolicNode& node) {
69 if (found) return;
70 const auto binder = detail::binder_view(node);
71 visit_children(binder->outside_scope);
72 if (!found && binder->bound_name != name_) visit_child(binder->scoped_body);
73 }
74
75 const std::string_view name_;
76 const bool antiderivatives_;
77};
78
80public:
81 using RecursiveSymbolicVisitor::RecursiveSymbolicVisitor;
82 std::set<std::string> names;
83
84 void visit(const VariableNode& node) override {
85 if (!node.is_constant()) names.insert(node.name());
86 }
87 void visit(const SummationNode& node) override {
88 names.insert(node.index_var());
90 }
91 void visit(const ProductNode& node) override {
92 names.insert(node.index_var());
94 }
95 void visit(const IntegralNode& node) override {
96 names.insert(node.variable());
98 }
99 void visit(const TransformNode& node) override {
100 names.insert(node.source_var());
102 }
103 void visit(const QuantifierNode& node) override {
104 names.insert(node.bound_var());
106 }
107 void visit(const SetBuilderNode& node) override {
108 names.insert(node.element_var());
110 }
111 void visit(const LimitNode& node) override {
112 names.insert(node.variable());
114 }
115 void visit(const RootOfNode&) override {}
116};
117
118std::set<std::string> all_names(const detail::SymbolicNodePtr& expression,
119 detail::RewriteBudget* budget = nullptr) {
120 AllNameCollector collector(budget);
121 if (expression) expression->accept(collector);
122 return std::move(collector.names);
123}
124
125std::string fresh_name(const std::string& base, std::set<std::string>& occupied) {
126 for (std::size_t suffix = 1;; ++suffix) {
127 auto candidate = base + "_" + std::to_string(suffix);
128 if (occupied.insert(candidate).second) return candidate;
129 }
130}
131
133public:
135 std::set<std::string> replacement_free,
136 std::set<std::string> occupied, detail::RewriteBudget* budget)
137 : SymbolicRewriter(budget), target_(std::move(target)),
138 replacement_(std::move(replacement)),
139 replacement_free_(std::move(replacement_free)),
140 occupied_(std::move(occupied)) {}
141
142 void visit(const VariableNode& node) override {
143 set_result(!node.is_constant() && node.name() == target_ ? replacement_ : current());
144 }
145
146 void visit(const SummationNode& node) override {
147 bool changed = false;
148 auto lower = rewrite_child(node.lower_bound(), changed);
149 auto upper = rewrite_child(node.upper_bound(), changed);
150 auto binder = node.index_var();
151 auto body = rewrite_scoped(node.body(), binder, changed);
153 : current());
154 }
155
156 void visit(const ProductNode& node) override {
157 bool changed = false;
158 auto lower = rewrite_child(node.lower_bound(), changed);
159 auto upper = rewrite_child(node.upper_bound(), changed);
160 auto binder = node.index_var();
161 auto body = rewrite_scoped(node.body(), binder, changed);
162 set_result(changed ? detail::make_node<ProductNode>(body, binder, lower, upper)
163 : current());
164 }
165
166 void visit(const IntegralNode& node) override {
167 bool changed = false;
168 auto lower = node.lower() ? rewrite_child(node.lower(), changed) : nullptr;
169 auto upper = node.upper() ? rewrite_child(node.upper(), changed) : nullptr;
170 auto binder = node.variable();
171 auto body = rewrite_scoped(node.body(), binder, changed);
172 set_result(changed ? detail::make_node<IntegralNode>(body, binder, lower, upper)
173 : current());
174 }
175
176 void visit(const TransformNode& node) override {
177 bool changed = false;
178 auto target = rewrite_child(node.target(), changed);
179 auto binder = node.source_var();
180 auto body = rewrite_scoped(node.body(), binder, changed);
181 set_result(changed ? detail::make_node<TransformNode>(node.transform_type(), body,
182 binder, target)
183 : current());
184 }
185
186 void visit(const QuantifierNode& node) override {
187 bool changed = false;
188 auto domain = rewrite_child(node.domain(), changed);
189 auto binder = node.bound_var();
190 auto predicate = rewrite_scoped(node.predicate(), binder, changed);
191 set_result(changed ? detail::make_node<QuantifierNode>(node.quantifier_type(), binder,
192 domain, predicate)
193 : current());
194 }
195
196 void visit(const SetBuilderNode& node) override {
197 bool changed = false;
198 auto domain = rewrite_child(node.domain(), changed);
199 auto binder = node.element_var();
200 auto predicate = rewrite_scoped(node.predicate(), binder, changed);
201 set_result(changed ? detail::make_node<SetBuilderNode>(binder, domain, predicate)
202 : current());
203 }
204
205 void visit(const LimitNode& node) override {
206 bool changed = false;
207 auto point = rewrite_child(node.point(), changed);
208 auto binder = node.variable();
209 auto body = rewrite_scoped(node.body(), binder, changed);
211 body, binder, point, node.direction())
212 : current());
213 }
214
215 void visit(const RootOfNode&) override { set_result(current()); }
216
217private:
219 const detail::SymbolicNodePtr& original_body,
220 std::string& binder,
221 bool& changed) {
222 if (binder == target_) return original_body;
223
224 auto body = original_body;
226 replacement_free_.find(binder) != replacement_free_.end()) {
227 const auto renamed = fresh_name(binder, occupied_);
228 body = substitute_free(body, binder,
230 binder = renamed;
231 changed = true;
232 }
233 return rewrite_child(body, changed);
234 }
235
236 std::string target_;
238 const std::set<std::string> replacement_free_;
239 std::set<std::string> occupied_;
240};
241
242} // namespace
243std::optional<Rational> detail::exact_rational_value(
244 const NumberNode& number) {
245 if (const auto* integer = std::get_if<BigInt>(&number.value())) {
246 return Rational(*integer);
247 }
248 if (const auto* rational = std::get_if<Rational>(&number.value())) {
249 return *rational;
250 }
251 return std::nullopt;
252}
253
254std::optional<Rational> detail::exact_rational_value(
256 const auto number = std::dynamic_pointer_cast<const NumberNode>(node);
257 return number ? exact_rational_value(*number) : std::nullopt;
258}
259
260
262 const SymbolicExpr& expression, const std::string& variable,
264 using AffineResult = Result<std::optional<AffineForm>>;
265 auto converted = symbolic_to_poly_checked(expression, variable, context);
266 if (!converted) {
267 if (converted.error().code == CasErrc::UnsupportedExpression) {
268 return std::optional<AffineForm>{};
269 }
270 return AffineResult::failure(converted.error());
271 }
272 const auto& polynomial = converted.value();
273 if (polynomial.degree() > 1) return std::optional<AffineForm>{};
274 return std::optional<AffineForm>{AffineForm{
275 polynomial.coeffs.size() > 1 ? polynomial.coeffs[1].val : SymbolicExpr::number(0),
276 polynomial.coeffs.empty() ? SymbolicExpr::number(0) : polynomial.coeffs[0].val}};
277}
278
279std::optional<int> exact_small_integer_node(
280 const detail::SymbolicNodePtr& node, int min_value, int max_value) {
281 auto number = std::dynamic_pointer_cast<const NumberNode>(node);
282 if (!number) return std::nullopt;
283
284 BigInt value;
285 if (std::holds_alternative<BigInt>(number->value())) {
286 value = std::get<BigInt>(number->value());
287 } else if (std::holds_alternative<Rational>(number->value())) {
288 const Rational& rational = std::get<Rational>(number->value());
289 if (!rational.is_integer()) return std::nullopt;
290 value = rational.to_bigint();
291 } else {
292 return std::nullopt;
293 }
294
295 const auto exact = value.try_to_int64();
296 if (!exact || *exact < min_value || *exact > max_value) {
297 return std::nullopt;
298 }
299 return static_cast<int>(*exact);
300}
301
302bool detail::is_imaginary_unit_name(const std::string& name) {
303 return name == "I";
304}
305
306std::set<std::string> detail::all_variable_names(
308 if (budget) { budget->measure(expression); }
309 return all_names(expression, budget);
310}
311
312std::set<std::string> free_variables(const detail::SymbolicNodePtr& expression,
313 detail::RewriteBudget* budget) {
314 FreeVariableCollector collector(budget);
315 if (expression) expression->accept(collector);
316 return std::move(collector.variables);
317}
318
320 const detail::SymbolicNodePtr& expression,
321 const std::string& variable, detail::RewriteBudget* budget) {
322 if (!expression || variable.empty()) return false;
323 FreeVariableFinder finder(variable, budget);
324 expression->accept(finder);
325 return finder.found;
326}
327
329 const SymbolicNodePtr& expression, const std::string& variable, RewriteBudget* budget) {
330 if (!expression || variable.empty()) return false;
331 FreeVariableFinder finder(variable, budget, true);
332 expression->accept(finder);
333 return finder.found;
334}
335
337 const detail::SymbolicNodePtr& expression,
338 const std::string& variable,
339 const detail::SymbolicNodePtr& replacement, detail::RewriteBudget* budget) {
340 if (!expression) return nullptr;
341 if (variable.empty()) throw std::invalid_argument("substitution variable cannot be empty");
342 if (!replacement) throw std::invalid_argument("substitution replacement cannot be null");
343
344 auto replacement_free = free_variables(replacement, budget);
345 auto occupied = all_names(expression, budget);
346 auto replacement_names = all_names(replacement, budget);
347 occupied.insert(replacement_names.begin(), replacement_names.end());
348 occupied.insert(variable);
349
350 FreeSubstitution substitution(variable, replacement,
351 std::move(replacement_free),
352 std::move(occupied), budget);
353 return substitution.rewrite(expression);
354}
355
356} // namespace LMCAS
const std::variant< BigInt, Rational > & value() const noexcept
连乘节点,表示符号有限/无限连乘 prod_{k=a}^{b} f(k).
求和节点,表示符号有限/无限求和 sum_{k=a}^{b} f(k).
static std::shared_ptr< const SymbolicNode > create_variable(const std::string &name)
virtual void accept(LMCAS::detail::SymbolicVisitor &visitor) const =0
积分变换节点,表示 Laplace,Fourier,Z 变换及其逆变换.
变量节点,表示一个符号变量。
FreeSubstitution(std::string target, detail::SymbolicNodePtr replacement, std::set< std::string > replacement_free, std::set< std::string > occupied, detail::RewriteBudget *budget)
detail::SymbolicNodePtr rewrite_scoped(const detail::SymbolicNodePtr &original_body, std::string &binder, bool &changed)
FreeVariableFinder(std::string_view name, detail::RewriteBudget *budget=nullptr, bool antiderivatives=false)
void visit(const NumberNode &) override
virtual void visit_child(const SymbolicNodePtr &child)
void visit_children(const Range &children)
std::size_t measure(const std::shared_ptr< const SymbolicNode > &node)
void set_result(SymbolicNodePtr result)
SymbolicNodePtr rewrite_child(const SymbolicNodePtr &child, bool &changed)
SymbolicNodePtr current() const noexcept
RewriteBudget * rewrite_budget() const noexcept
std::string fresh_name(const std::string &base, std::set< std::string > &occupied)
std::set< std::string > all_names(const detail::SymbolicNodePtr &expression, detail::RewriteBudget *budget=nullptr)
Result< Polynomial< SymbolicPolyCoeff > > symbolic_to_poly_checked(const SymbolicExpr &expression, const std::string &variable, ComputationContext &context)
bool is_imaginary_unit_name(const std::string &name)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
std::optional< BinderView > binder_view(const SymbolicNode &node)
const std::string & variable
std::shared_ptr< const SymbolicNode > SymbolicNodePtr
bool integration_depends_on_variable(const SymbolicNodePtr &expression, const std::string &variable, RewriteBudget *budget=nullptr)
std::shared_ptr< const Node > make_node(Args &&... args)
std::optional< Rational > exact_rational_value(const NumberNode &number)
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)
const std::string const LimitExprPtr & point
const std::string & variable
__attribute__((visibility("default"))) ExprResult function(const std __attribute__((visibility("default"))) ExprResult finite_set(std ExprPtr upper
ExprResult integer(Integer)=delete
const ExprPtr & lower
bool expression_depends_on_variable(const detail::SymbolicNodePtr &expression, const std::string &variable, detail::RewriteBudget *budget)
std::optional< int > exact_small_integer_node(const detail::SymbolicNodePtr &node, int min_value, int max_value)
const NumberDomainSet & domain
std::set< std::string > free_variables(const detail::SymbolicNodePtr &expression, detail::RewriteBudget *budget)
const ExprPtr & target
const std::string const ExprPtr & value
__attribute__((visibility("default"))) Result< EqvProfile > eqv_profile_from_name(const std const std::string & name
ExprResult rational(const Rational &value)
detail::SymbolicNodePtr substitute_free(const detail::SymbolicNodePtr &expression, const std::string &variable, const detail::SymbolicNodePtr &replacement, detail::RewriteBudget *budget)