LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
assumption_context.cpp
浏览该文件的文档.
6#include <limits>
7#include <set>
8
9namespace LMCAS {
10
11namespace {
12
13bool has_value_declaration(const PropertyStore& properties, const std::string& symbol) {
14 return !properties.get_signs(symbol).empty() ||
15 properties.get_domain(symbol) != Domain::Complex ||
16 properties.get_parity(symbol) != Parity::Unknown ||
17 properties.get_boundedness(symbol) != Boundedness::Unknown ||
18 properties.get_finiteness(symbol) != Finiteness::Unknown;
19}
20
22 const std::vector<std::array<std::size_t, 2>>& alternatives,
23 const std::vector<bool>& visible, detail::RewriteBudget& budget) {
24 for (const auto& premises : alternatives) {
25 auto step = budget.context().consume_steps(1, "assumption.relations");
26 if (!step) throw step.error();
27 if (visible[premises[0]] && visible[premises[1]]) return true;
28 }
29 return false;
30}
31
32}
33
34
35AssumptionContext::AssumptionContext() {
36 scope_stack_.emplace_back();
37}
38
39AssumptionContext::AssumptionContext(AssumptionContext&& other)
40 : AssumptionContext() {
41 scope_stack_.swap(other.scope_stack_);
42 max_query_depth_ = other.max_query_depth_;
43 ++other.cache_generation_;
44}
45
46AssumptionContext& AssumptionContext::operator=(const AssumptionContext& other) {
47 if (this != &other) {
48 auto candidate = other.scope_stack_;
49 scope_stack_.swap(candidate);
50 max_query_depth_ = other.max_query_depth_;
51 ++cache_generation_;
52 }
53 return *this;
54}
55
56AssumptionContext& AssumptionContext::operator=(AssumptionContext&& other) noexcept {
57 if (this != &other) {
58 scope_stack_.swap(other.scope_stack_);
59 std::swap(max_query_depth_, other.max_query_depth_);
60 ++cache_generation_;
61 ++other.cache_generation_;
62 }
63 return *this;
64}
65
66uint64_t AssumptionContext::cache_generation() const {
67 bool changed = false;
68 for (const auto& scope : scope_stack_) {
69 const auto properties = scope.properties.revision();
70 const auto relations = scope.relations.revision();
71 if (scope.observed_property_revision != properties ||
72 scope.observed_relation_revision != relations) {
73 scope.observed_property_revision = properties;
74 scope.observed_relation_revision = relations;
75 changed = true;
76 }
77 }
78 if (changed) ++cache_generation_;
79 return cache_generation_;
80}
81
82// Scope management
83
84void AssumptionContext::push() {
85 scope_stack_.emplace_back();
86 ++cache_generation_;
87}
88
89AssumptionVoidResult AssumptionContext::pop() {
90 if (scope_stack_.size() <= 1) {
92 CasErrc::InvalidArgument, "cannot pop the root assumption scope", "assumption.pop");
93 }
94 scope_stack_.pop_back();
95 ++cache_generation_;
97}
98
99int AssumptionContext::depth() const {
100 return static_cast<int>(scope_stack_.size());
101}
102
103// Direct access to current (top) scope stores
104
105PropertyStore& AssumptionContext::current_properties() {
106 return scope_stack_.back().properties;
107}
108
109const PropertyStore& AssumptionContext::current_properties() const {
110 return scope_stack_.back().properties;
111}
112
113RelationStore& AssumptionContext::current_relations() {
114 return scope_stack_.back().relations;
115}
116
117const RelationStore& AssumptionContext::current_relations() const {
118 return scope_stack_.back().relations;
119}
120
121// Read-through query methods
122
123bool AssumptionContext::has_sign(const std::string& symbol, Sign sign) const {
124 for (auto it = scope_stack_.rbegin(); it != scope_stack_.rend(); ++it) {
125 auto signs = it->properties.get_signs(symbol);
126 if (!signs.empty()) return signs.count(sign) > 0;
127 }
128 return false;
129}
130
131bool AssumptionContext::has_domain(const std::string& symbol, Domain domain) const {
132 for (auto it = scope_stack_.rbegin(); it != scope_stack_.rend(); ++it) {
133 const auto declared_domain = it->properties.get_domain(symbol);
134 if (declared_domain != Domain::Complex) {
135 return it->properties.has_domain(symbol, domain);
136 }
137 }
138 return domain == Domain::Complex;
139}
140
141Domain AssumptionContext::get_domain(const std::string& symbol) const {
142 for (auto it = scope_stack_.rbegin(); it != scope_stack_.rend(); ++it) {
143 const auto domain = it->properties.get_domain(symbol);
144 if (domain != Domain::Complex) return domain;
145 }
146 return Domain::Complex;
147}
148
149std::unordered_set<Sign, SignHash> AssumptionContext::get_signs(
150 const std::string& symbol) const {
151 for (auto it = scope_stack_.rbegin(); it != scope_stack_.rend(); ++it) {
152 auto signs = it->properties.get_signs(symbol);
153 if (!signs.empty()) return signs;
154 }
155 return {};
156}
157
158Parity AssumptionContext::get_parity(const std::string& symbol) const {
159 for (auto it = scope_stack_.rbegin(); it != scope_stack_.rend(); ++it) {
160 const auto parity = it->properties.get_parity(symbol);
161 if (parity != Parity::Unknown) return parity;
162 }
163 return Parity::Unknown;
164}
165
166Boundedness AssumptionContext::get_boundedness(const std::string& symbol) const {
167 for (auto it = scope_stack_.rbegin(); it != scope_stack_.rend(); ++it) {
168 const auto boundedness = it->properties.get_boundedness(symbol);
169 if (boundedness != Boundedness::Unknown) return boundedness;
170 }
172}
173
174std::optional<Interval> AssumptionContext::get_bounds(const std::string& symbol) const {
175 for (auto it = scope_stack_.rbegin(); it != scope_stack_.rend(); ++it) {
176 auto bounds = it->properties.get_bounds(symbol);
177 if (bounds) return bounds;
178 }
179 return std::nullopt;
180}
181
182bool AssumptionContext::is_transcendental(const std::string& symbol) const {
183 for (auto it = scope_stack_.rbegin(); it != scope_stack_.rend(); ++it) {
184 if (it->properties.is_transcendental(symbol)) return true;
185 if (it->properties.has_domain(symbol, Domain::Algebraic)) return false;
186 }
187 return false;
188}
189
190Finiteness AssumptionContext::get_finiteness(const std::string& symbol) const {
191 for (auto it = scope_stack_.rbegin(); it != scope_stack_.rend(); ++it) {
192 auto value = it->properties.get_finiteness(symbol);
193 if (value != Finiteness::Unknown) return value;
194 }
195 return Finiteness::Unknown;
196}
197
198Definiteness AssumptionContext::get_definiteness(const std::string& symbol) const {
199 for (auto it = scope_stack_.rbegin(); it != scope_stack_.rend(); ++it) {
200 auto value = it->properties.get_definiteness(symbol);
201 if (value != Definiteness::Unknown) return value;
202 }
204}
205
206std::optional<SymbolicExpr> AssumptionContext::get_period(
207 const std::string& symbol, const std::string& variable) const {
208 for (auto it = scope_stack_.rbegin(); it != scope_stack_.rend(); ++it) {
209 auto value = it->properties.get_period(symbol, variable);
210 if (value) return value;
211 }
212 return std::nullopt;
213}
214
215bool AssumptionContext::is_periodic(const std::string& symbol, const std::string& variable) const {
216 return get_period(symbol, variable).has_value();
217}
218
219bool AssumptionContext::has_period_declarations(const std::string& symbol) const {
220 for (auto it = scope_stack_.rbegin(); it != scope_stack_.rend(); ++it) {
221 if (!it->properties.get_period_decls(symbol).empty()) return true;
222 }
223 return false;
224}
225
226Result<Monotonicity> AssumptionContext::get_monotonicity_checked(
227 const std::string& symbol, const std::string& variable,
228 const Interval& interval) const {
230 for (auto it = scope_stack_.rbegin(); it != scope_stack_.rend(); ++it) {
231 auto value = it->properties.get_monotonicity_checked(
232 symbol, variable, interval, context);
233 if (!value || value.value() != Monotonicity::Unknown) return value;
234 }
236}
237
239 std::set<std::string> symbols;
240 std::vector<SymbolicExpr> expressions;
241};
242
243AssumptionContext::RelationShadowing AssumptionContext::collect_relation_shadowing(
244 std::size_t scope, detail::RewriteBudget& budget) const {
245 RelationShadowing shadowing;
246 for (std::size_t i = scope + 1; i < scope_stack_.size(); ++i) {
247 auto step = budget.context().consume_steps(1, "assumption.relations");
248 if (!step) throw step.error();
249 const auto& nearer = scope_stack_[i];
250 const auto& store = nearer.relations;
251 for (std::size_t j = 0; j < store.relations_.size(); ++j) {
252 step = budget.context().consume_steps(1, "assumption.relations");
253 if (!step) throw step.error();
254 if (!store.proofs_[j].declared) continue;
255 const auto& declared = store.relations_[j];
256 const SymbolicExpr* subject = nullptr;
257 if (!free_variables(detail::node(declared.lhs), &budget).empty()) {
258 subject = &declared.lhs;
259 } else if (!free_variables(detail::node(declared.rhs), &budget).empty()) {
260 subject = &declared.rhs;
261 }
262 if (!subject) continue;
263 if (const auto* variable =
264 dynamic_cast<const VariableNode*>(detail::node(*subject).get())) {
265 shadowing.symbols.insert(variable->name());
266 } else {
267 shadowing.expressions.push_back(*subject);
268 }
269 }
270 for (const auto& symbol : nearer.properties.get_all_symbols()) {
271 step = budget.context().consume_steps(1, "assumption.relations");
272 if (!step) throw step.error();
273 if (has_value_declaration(nearer.properties, symbol)) {
274 shadowing.symbols.insert(symbol);
275 }
276 }
277 }
278 return shadowing;
279}
280
281bool AssumptionContext::relation_shadowed(
282 const Relation& relation, const RelationShadowing& shadowing,
283 detail::RewriteBudget& budget) {
284 const auto& lhs = detail::node(relation.lhs);
285 const auto& rhs = detail::node(relation.rhs);
286 for (const auto& symbol : shadowing.symbols) {
287 auto step = budget.context().consume_steps(1, "assumption.relations");
288 if (!step) { throw step.error(); }
289 if (expression_depends_on_variable(lhs, symbol, &budget) ||
290 expression_depends_on_variable(rhs, symbol, &budget)) { return true; }
291 }
292 if (shadowing.expressions.empty()) { return false; }
293 budget.measure(lhs);
295 for (const auto& expression : shadowing.expressions) {
296 auto step = budget.context().consume_steps(1, "assumption.relations");
297 if (!step) { throw step.error(); }
298 const auto& subject = detail::node(expression);
299 budget.measure(subject);
300 if ((lhs && lhs->equals(*subject)) || (rhs && rhs->equals(*subject))) {
301 return true;
302 }
303 }
304 return false;
305}
306
307std::vector<bool> AssumptionContext::relation_visibility(
308 std::size_t scope, detail::RewriteBudget& budget) const {
309 // An empty mask denotes that every relation in the top scope is visible.
310 if (scope + 1 == scope_stack_.size()) return {};
311 const auto& store = scope_stack_[scope].relations;
312 const auto shadowing = collect_relation_shadowing(scope, budget);
313 std::vector<bool> unshadowed(store.relations_.size(), false);
314 std::vector<bool> visible(store.relations_.size(), false);
315 for (std::size_t i = 0; i < store.relations_.size(); ++i) {
316 auto step = budget.context().consume_steps(1, "assumption.relations");
317 if (!step) throw step.error();
318 unshadowed[i] = !relation_shadowed(store.relations_[i], shadowing, budget);
319 visible[i] = unshadowed[i] && store.proofs_[i].declared;
320 }
321 bool changed;
322 do {
323 auto step = budget.context().consume_steps(1, "assumption.relations");
324 if (!step) throw step.error();
325 changed = false;
326 for (std::size_t i = 0; i < store.relations_.size(); ++i) {
327 step = budget.context().consume_steps(1, "assumption.relations");
328 if (!step) throw step.error();
329 if (!unshadowed[i] || visible[i]) continue;
330 if (!has_visible_proof(store.proofs_[i].alternatives, visible, budget)) continue;
331 visible[i] = true;
332 changed = true;
333 }
334 } while (changed);
335 return visible;
336}
337
338Result<bool> AssumptionContext::has_relation_in_scope(
339 std::size_t scope, const SymbolicExpr& lhs, const SymbolicExpr& rhs,
340 RelationOp op, detail::RewriteBudget& budget) const {
341 const auto& relations = scope_stack_[scope].relations.get_relations();
342 const auto visible = relation_visibility(scope, budget);
343 const auto& left = detail::node(lhs);
344 const auto& right = detail::node(rhs);
345 for (std::size_t j = 0; j < relations.size(); ++j) {
346 auto step = budget.context().consume_steps(1, "assumption.has_relation");
347 if (!step) { return Result<bool>::failure(step.error()); }
348 const auto& relation = relations[j];
349 if (relation.op != op || (!visible.empty() && !visible[j])) { continue; }
350 const auto& relation_lhs = detail::node(relation.lhs);
351 const auto& relation_rhs = detail::node(relation.rhs);
352 if (!relation_lhs || !relation_rhs) { continue; }
353 budget.measure(relation_lhs);
354 budget.measure(relation_rhs);
355 if (left->equals(*relation_lhs) && right->equals(*relation_rhs)) { return true; }
356 }
357 return false;
358}
359
360bool AssumptionContext::has_relation(
361 const SymbolicExpr& lhs, const SymbolicExpr& rhs, RelationOp op) const {
363 auto result = has_relation_checked(lhs, rhs, op, context);
364 if (!result) throw result.error();
365 return result.value();
366}
367
368Result<bool> AssumptionContext::has_relation_checked(
369 const SymbolicExpr& lhs, const SymbolicExpr& rhs, RelationOp op,
371 try {
372 detail::RewriteBudget budget(context, context.limits().max_recursion_depth,
373 std::numeric_limits<std::size_t>::max(), "assumption.has_relation");
374 auto step = context.consume_steps(1, "assumption.has_relation");
375 if (!step) { return Result<bool>::failure(step.error()); }
376 const auto& left = detail::node(lhs);
377 const auto& right = detail::node(rhs);
378 if (!left || !right) { return false; }
379 budget.measure(left);
381 for (std::size_t i = scope_stack_.size(); i-- > 0;) {
382 step = context.consume_steps(1, "assumption.has_relation");
383 if (!step) { return Result<bool>::failure(step.error()); }
384 auto match = has_relation_in_scope(i, lhs, rhs, op, budget);
385 if (!match || match.value()) { return match; }
386 }
387 return false;
388 } catch (const CasError& error) {
389 return Result<bool>::failure(error);
390 } catch (const std::bad_alloc&) {
392 "relation query allocation failed", "assumption.has_relation");
393 }
394}
395
396std::vector<Relation> AssumptionContext::get_visible_relations() const {
398 auto result = get_visible_relations_checked(context);
399 if (!result) throw result.error();
400 return std::move(result.value());
401}
402
403Result<std::vector<Relation>> AssumptionContext::get_visible_relations_checked(
405 try {
406 detail::RewriteBudget budget(context, context.limits().max_recursion_depth,
407 std::numeric_limits<std::size_t>::max(), "assumption.visible_relations");
408 std::vector<Relation> visible;
409 for (std::size_t i = scope_stack_.size(); i-- > 0;) {
410 auto step = context.consume_steps(1, "assumption.visible_relations");
411 if (!step) return Result<std::vector<Relation>>::failure(step.error());
412 const auto& relations = scope_stack_[i].relations.get_relations();
413 const auto visibility = relation_visibility(i, budget);
414 for (std::size_t j = 0; j < relations.size(); ++j) {
415 step = context.consume_steps(1, "assumption.visible_relations");
416 if (!step) return Result<std::vector<Relation>>::failure(step.error());
417 if (visibility.empty() || visibility[j]) visible.push_back(relations[j]);
418 }
419 }
420 return visible;
421 } catch (const CasError& error) {
422 return Result<std::vector<Relation>>::failure(error);
423 } catch (const std::bad_alloc&) {
425 "relation query allocation failed", "assumption.visible_relations");
426 }
427}
428
429AssumptionVoidResult AssumptionContext::assume_domain(
430 const std::string& variable,
431 Domain domain) {
432 return assume_domain_checked(variable, domain);
433}
434
435AssumptionVoidResult AssumptionContext::assume_domain_checked(
436 const std::string& variable,
437 Domain domain) {
438 constexpr const char* operation = "assume_domain";
439 if (variable.empty()) {
441 CasErrc::InvalidArgument, "variable name must not be empty", operation);
442 }
443 auto result = scope_stack_.back().properties.declare_domain_checked(variable, domain);
444 if (!result) {
446 result.error().code, result.error().message, operation);
447 }
449}
450
451AssumptionVoidResult AssumptionContext::assume_sign(
452 const std::string& variable,
453 Sign sign) {
454 return assume_sign_checked(variable, sign);
455}
456
457AssumptionVoidResult AssumptionContext::assume_sign_checked(
458 const std::string& variable,
459 Sign sign) {
460 constexpr const char* operation = "assume_sign";
461 if (variable.empty()) {
463 CasErrc::InvalidArgument, "variable name must not be empty", operation);
464 }
465 auto result = scope_stack_.back().properties.declare_sign_checked(variable, sign);
466 if (!result) {
468 result.error().code, result.error().message, operation);
469 }
471}
472
473AssumptionVoidResult AssumptionContext::assume(const SymbolicExpr& relation) {
474 return assume_checked(relation);
475}
476
477AssumptionVoidResult AssumptionContext::assume_checked(const SymbolicExpr& relation) {
478 constexpr const char* operation = "assume";
481 CasErrc::InvalidArgument, "relation expression must not be null", operation);
482 }
483 if (!std::dynamic_pointer_cast<const RelationalNode>(LMCAS::detail::node(relation))) {
485 CasErrc::InvalidArgument, "relation expression root must be relational", operation);
486 }
487 try {
488 auto rel_node = std::dynamic_pointer_cast<const RelationalNode>(LMCAS::detail::node(relation));
489 auto lhs = LMCAS::detail::expression_from_node(rel_node->left());
490 auto rhs = LMCAS::detail::expression_from_node(rel_node->right());
491 auto result = scope_stack_.back().relations.add_relation_checked(
492 lhs, rhs, rel_node->op(), scope_stack_.back().properties);
493 if (!result.has_value()) {
494 return AssumptionVoidResult::failure(result.error());
495 }
496 } catch (const std::bad_alloc&) {
498 CasErrc::ResourceLimit, "assumption allocation failed", operation);
499 } catch (const std::invalid_argument& ex) {
500 return AssumptionVoidResult::failure(CasErrc::InvalidArgument, ex.what(), operation);
501 } catch (const std::exception& ex) {
502 return AssumptionVoidResult::failure(CasErrc::InternalInvariant, ex.what(), operation);
503 }
505}
506// Depth limit configuration
507
508void AssumptionContext::set_max_query_depth(int depth) {
509 if (depth <= 0 || depth == max_query_depth_) return;
510 max_query_depth_ = depth;
511 ++cache_generation_;
512}
513
514int AssumptionContext::get_max_query_depth() const {
515 return max_query_depth_;
516}
517
518}
AssumptionContext class with scoped push/pop management.
Result< void > consume_steps(std::size_t amount=1, const std::string &operation={})
const CasError & error() const
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success()
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(T value)
变量节点,表示一个符号变量。
ComputationContext & context() const noexcept
std::size_t measure(const std::shared_ptr< const SymbolicNode > &node)
bool has_visible_proof(const std::vector< std::array< std::size_t, 2 > > &alternatives, const std::vector< bool > &visible, detail::RewriteBudget &budget)
bool has_value_declaration(const PropertyStore &properties, const std::string &symbol)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
inline ::LMCAS::SymbolicExpr expression_from_node(SymbolicNodePtr root)
ComputationContext const std::string & operation
const std::string & variable
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
ExprResult interval(ExprPtr lower, ExprPtr upper, bool lower_closed, bool upper_closed)
Domain
Mathematical domain hierarchy (least specific → most specific). Complex ⊃ Real ⊃ Algebraic ⊃ Rational...
@ Complex
Complex numbers (default, least specific)
@ Algebraic
Algebraic numbers (roots of polynomials with rational coefficients)
Boundedness
Boundedness classification for symbols.
@ Unknown
Boundedness not determined
bool expression_depends_on_variable(const detail::SymbolicNodePtr &expression, const std::string &variable, detail::RewriteBudget *budget)
@ Unknown
Monotonicity not determined
Definiteness
Matrix definiteness classification.
@ Unknown
Definiteness not determined
RelationOp
Stable relational operator used by public APIs.
const ExprPtr RelationOp op
std::size_t std::size_t depth
const NumberDomainSet & domain
std::set< std::string > free_variables(const detail::SymbolicNodePtr &expression, detail::RewriteBudget *budget)
Finiteness
Finiteness classification for limits and sequences.
@ Unknown
Finiteness not determined
const ExprPtr & right
Result< void > AssumptionVoidResult
ExprResult relation(const ExprPtr &lhs, const ExprPtr &rhs, RelationOp op)
const std::string const ExprPtr & value
const ExprPtr & rhs
Parity
Integer parity classification.
@ Unknown
Parity not determined
Sign
Sign classification for symbols and expressions.
内部 AST 定义。