LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
property_store.cpp
浏览该文件的文档.
3#include <algorithm>
4
5namespace LMCAS {
6
7using namespace property_detail;
8
9PropertyStore::PropertyStore(PropertyStore&& other) noexcept
10 : properties_(std::move(other.properties_)) {
11 ++other.revision_;
12}
13
14PropertyStore& PropertyStore::operator=(const PropertyStore& other) {
15 if (this != &other) {
16 auto candidate = other.properties_;
17 properties_.swap(candidate);
18 ++revision_;
19 }
20 return *this;
21}
22
23PropertyStore& PropertyStore::operator=(PropertyStore&& other) noexcept {
24 if (this != &other) {
25 properties_ = std::move(other.properties_);
26 ++revision_;
27 ++other.revision_;
28 }
29 return *this;
30}
31
32namespace {
33
38
39std::string sign_str(Sign s) {
40 switch (s) {
41 case Sign::Positive: return "Positive";
42 case Sign::Negative: return "Negative";
43 case Sign::NonNegative: return "NonNegative";
44 case Sign::NonPositive: return "NonPositive";
45 case Sign::Zero: return "Zero";
46 case Sign::NonZero: return "NonZero";
47 }
48 return "Unknown";
49}
51 struct Conflict {
52 Sign incoming;
53 std::initializer_list<Sign> existing;
54 };
55 const Conflict conflicts[] = {
62 };
63 for (const auto& conflict : conflicts) {
64 if (conflict.incoming != a) {
65 continue;
66 }
67 return std::find(conflict.existing.begin(), conflict.existing.end(), b)
68 != conflict.existing.end();
69 }
70 return false;
71}
72
73}
74
75int PropertyStore::domain_specificity(Domain domain) const {
76 for (int i = 0; i < 7; ++i) {
77 if (domain_order[i] == domain) return i;
78 }
79 return 0;
80}
81
82
83// Sign implication and contradiction
84
85std::unordered_set<Sign, SignHash> PropertyStore::get_implied_signs(Sign sign) const {
86 std::unordered_set<Sign, SignHash> implied;
87 switch (sign) {
88 case Sign::Positive:
89 implied.insert(Sign::NonNegative);
90 implied.insert(Sign::NonZero);
91 break;
92 case Sign::Negative:
93 implied.insert(Sign::NonPositive);
94 implied.insert(Sign::NonZero);
95 break;
96 case Sign::Zero:
97 implied.insert(Sign::NonNegative);
98 implied.insert(Sign::NonPositive);
99 break;
102 case Sign::NonZero:
103 break;
104 }
105 return implied;
106}
107
108void PropertyStore::check_sign_contradiction(const std::string& symbol,
109 const std::unordered_set<Sign, SignHash>& existing,
110 Sign new_sign) {
111 for (Sign existing_sign : existing) {
112 if (signs_conflict(new_sign, existing_sign)) {
113 throw std::invalid_argument(
114 "Contradiction for symbol '" + symbol + "': cannot declare sign " +
115 sign_str(new_sign) + " because it conflicts with existing sign " +
116 sign_str(existing_sign));
117 }
118 }
119}
120
121void PropertyStore::check_domain_sign_consistency(const std::string& symbol,
122 const SymbolProperties& props,
123 Domain new_domain) {
124 // Natural is incompatible with Negative
125 if (new_domain == Domain::Natural || new_domain == Domain::PositiveInt) {
126 if (props.signs.count(Sign::Negative)) {
127 throw std::invalid_argument(
128 "Contradiction for symbol '" + symbol +
129 "': domain " + domain_str(new_domain) +
130 " is incompatible with existing sign Negative");
131 }
132 }
133
134 // PositiveInt is additionally incompatible with Zero and NonPositive
135 if (new_domain == Domain::PositiveInt) {
136 if (props.signs.count(Sign::Zero)) {
137 throw std::invalid_argument(
138 "Contradiction for symbol '" + symbol +
139 "': domain PositiveInt is incompatible with existing sign Zero");
140 }
141 if (props.signs.count(Sign::NonPositive)) {
142 throw std::invalid_argument(
143 "Contradiction for symbol '" + symbol +
144 "': domain PositiveInt is incompatible with existing sign NonPositive");
145 }
146 }
147}
148
149// declare_domain
150
151void PropertyStore::declare_domain_unchecked(const std::string& symbol, Domain domain) {
152 auto& props = properties_[symbol];
153
154 // Idempotent: same domain already set -> no-op
155 if (props.most_specific_domain == domain) {
156 return;
157 }
158
159 // Specificity preservation: if the new domain is an ancestor (less specific)
160 // of the current most-specific domain, it's a no-op.
161 if (domain_specificity(domain) < domain_specificity(props.most_specific_domain)) {
162 return;
163 }
164
165 /// 超越域约束将符号域限制为 Real 或 Complex.
166 if (props.transcendental && domain_specificity(domain) > domain_specificity(Domain::Real)) {
167 throw std::invalid_argument(
168 "Contradiction for symbol '" + symbol +
169 "': cannot declare domain " + domain_str(domain) +
170 " because symbol is Transcendental (existing domain: " +
171 domain_str(props.most_specific_domain) + ")");
172 }
173
174 // Check domain-sign cross-constraints before applying
175 check_domain_sign_consistency(symbol, props, domain);
176
177 // Set the new most-specific domain
178 props.most_specific_domain = domain;
179}
180
181PropertyStoreResult PropertyStore::declare_domain(
182 const std::string& symbol, Domain domain) {
183 return declare_domain_checked(symbol, domain);
184}
185
186PropertyStoreResult PropertyStore::declare_domain_checked(
187 const std::string& symbol,
188 Domain domain) {
189 return checked_property_update(*this, symbol, "declare_domain",
190 [&](PropertyStore& candidate) {
191 candidate.declare_domain_unchecked(symbol, domain);
192 });
193}
194
195// declare_sign
196
197void PropertyStore::declare_sign_unchecked(const std::string& symbol, Sign sign) {
198 auto& props = properties_[symbol];
199
200 // Idempotent: if sign already present, no-op
201 if (props.signs.count(sign)) {
202 return;
203 }
204
205 // Collect all signs to add (the declared sign + its implications)
206 std::unordered_set<Sign, SignHash> to_add;
207 to_add.insert(sign);
208 auto implied = get_implied_signs(sign);
209 to_add.insert(implied.begin(), implied.end());
210
211 // Check each new sign against existing signs for contradictions
212 for (Sign s : to_add) {
213 if (!props.signs.count(s)) {
214 check_sign_contradiction(symbol, props.signs, s);
215 }
216 }
217
218 // Also check domain-sign cross-constraints for the new signs
219 if (props.most_specific_domain == Domain::Natural ||
220 props.most_specific_domain == Domain::PositiveInt) {
221 for (Sign s : to_add) {
222 if (s == Sign::Negative) {
223 throw std::invalid_argument(
224 "Contradiction for symbol '" + symbol +
225 "': cannot declare sign Negative because it conflicts with domain " +
226 domain_str(props.most_specific_domain));
227 }
228 }
229 }
230 if (props.most_specific_domain == Domain::PositiveInt) {
231 for (Sign s : to_add) {
232 if (s == Sign::Zero) {
233 throw std::invalid_argument(
234 "Contradiction for symbol '" + symbol +
235 "': cannot declare sign Zero because it conflicts with domain PositiveInt");
236 }
237 if (s == Sign::NonPositive) {
238 throw std::invalid_argument(
239 "Contradiction for symbol '" + symbol +
240 "': cannot declare sign NonPositive because it conflicts with domain PositiveInt");
241 }
242 }
243 }
244
245 // Store all signs
246 props.signs.insert(to_add.begin(), to_add.end());
247
248 // Zero implies Integer domain
249 if (sign == Sign::Zero) {
250 if (domain_specificity(Domain::Integer) > domain_specificity(props.most_specific_domain)) {
251 props.most_specific_domain = Domain::Integer;
252 }
253 }
254}
255
256PropertyStoreResult PropertyStore::declare_sign(
257 const std::string& symbol, Sign sign) {
258 return declare_sign_checked(symbol, sign);
259}
260
261PropertyStoreResult PropertyStore::declare_sign_checked(
262 const std::string& symbol,
263 Sign sign) {
264 return checked_property_update(*this, symbol, "declare_sign",
265 [&](PropertyStore& candidate) {
266 candidate.declare_sign_unchecked(symbol, sign);
267 });
268}
269
270// declare_parity
271
272void PropertyStore::declare_parity_unchecked(const std::string& symbol, Parity parity) {
273 auto& props = properties_[symbol];
274
275 // Idempotent: same parity already set -> no-op
276 if (props.parity == parity) {
277 return;
278 }
279
280 // Unknown can always be set
281 if (parity == Parity::Unknown) {
282 props.parity = parity;
283 return;
284 }
285
286 // Contradiction: Even vs Odd
287 if (props.parity != Parity::Unknown && props.parity != parity) {
288 throw std::invalid_argument(
289 "Contradiction for symbol '" + symbol + "': parity conflict (Even vs Odd)");
290 }
291
292 // Auto-promote to Integer domain when Even or Odd is declared
293 if (domain_specificity(Domain::Integer) > domain_specificity(props.most_specific_domain)) {
294 props.most_specific_domain = Domain::Integer;
295 }
296
297 props.parity = parity;
298}
299
300PropertyStoreResult PropertyStore::declare_parity(
301 const std::string& symbol, Parity parity) {
302 return declare_parity_checked(symbol, parity);
303}
304
305PropertyStoreResult PropertyStore::declare_parity_checked(
306 const std::string& symbol,
307 Parity parity) {
308 return checked_property_update(*this, symbol, "declare_parity",
309 [&](PropertyStore& candidate) {
310 candidate.declare_parity_unchecked(symbol, parity);
311 });
312}
313
314// declare_bounded
315
316void PropertyStore::declare_bounded_unchecked(const std::string& symbol,
317 Boundedness bounded,
318 std::optional<Interval> bounds) {
319 auto& props = properties_[symbol];
320
321 if (props.boundedness == bounded) {
322 if (bounded == Boundedness::Bounded && bounds) {
323 props.bounds = std::move(bounds);
324 }
325 return;
326 }
327
328 if (bounded == Boundedness::Unknown) {
329 props.boundedness = bounded;
330 props.bounds = std::nullopt;
331 return;
332 }
333
334 if (props.boundedness != Boundedness::Unknown && props.boundedness != bounded) {
335 throw std::invalid_argument(
336 "Contradiction for symbol '" + symbol +
337 "': boundedness conflict (Bounded vs Unbounded)");
338 }
339
340 props.boundedness = bounded;
341 props.bounds = std::move(bounds);
342}
343
344namespace {
345
347
349 const Interval& bounds, const std::optional<Interval>& existing,
351 constexpr const char* operation = "declare_bounded";
352 auto incoming = IntervalUnion::from_intervals_checked(
353 {bounds}, context);
354 if (!incoming) {
355 if (incoming.error().code != CasErrc::Inconclusive || existing) {
356 return BoundsCandidateResult::failure(incoming.error());
357 }
358 return BoundsCandidateResult::success(bounds);
359 }
360 if (incoming.value().is_empty()) {
363 "bounded interval must not be empty", operation);
364 }
365
366 std::optional<Interval> candidate =
367 incoming.value().intervals().front();
368 if (!existing) {
369 return BoundsCandidateResult::success(std::move(candidate));
370 }
371
372 auto current = IntervalUnion::from_intervals_checked(
373 {*existing}, context);
374 if (!current) {
375 return BoundsCandidateResult::failure(current.error());
376 }
377 auto intersection = current.value().intersect_checked(
378 incoming.value(), context);
379 if (!intersection) {
380 return BoundsCandidateResult::failure(intersection.error());
381 }
382 if (intersection.value().is_empty()) {
385 "bounded interval contradicts existing bounds", operation);
386 }
387 candidate = intersection.value().intervals().front();
388 return BoundsCandidateResult::success(std::move(candidate));
389}
390
391}
392
393
394PropertyStoreResult PropertyStore::declare_bounded(
395 const std::string& symbol,
396 Boundedness bounded,
397 std::optional<Interval> bounds) {
398 return declare_bounded_checked(symbol, bounded, std::move(bounds));
399}
400
401PropertyStoreResult PropertyStore::declare_bounded_checked(
402 const std::string& symbol,
403 Boundedness bounded,
404 std::optional<Interval> bounds) {
406 return declare_bounded_checked(
407 symbol, bounded, std::move(bounds), context);
408}
409
410PropertyStoreResult PropertyStore::declare_bounded_checked(
411 const std::string& symbol,
412 Boundedness bounded,
413 std::optional<Interval> bounds,
415 constexpr const char* operation = "declare_bounded";
416 if (symbol.empty()) {
417 return invalid_empty_symbol(operation);
418 }
419 if (bounds && bounded != Boundedness::Bounded) {
422 "interval bounds require Bounded classification", operation);
423 }
424
425 try {
426 std::optional<Interval> committed_bounds;
427 if (bounds) {
428 auto candidate = bounded_interval_candidate(
429 *bounds, get_bounds(symbol), context);
430 if (!candidate) {
431 return PropertyStoreResult::failure(candidate.error());
432 }
433 committed_bounds = std::move(candidate.value());
434 }
435
436 PropertyStore candidate = *this;
437 candidate.declare_bounded_unchecked(
438 symbol, bounded, std::move(committed_bounds));
439 *this = std::move(candidate);
441 } catch (const std::bad_alloc&) {
444 "property-store allocation failed", operation);
445 } catch (const std::invalid_argument& error) {
447 CasErrc::InvalidArgument, error.what(), operation);
448 } catch (const std::exception& error) {
450 CasErrc::InternalInvariant, error.what(), operation);
451 }
452}
453
454// Query methods
455
456Domain PropertyStore::get_domain(const std::string& symbol) const {
457 auto it = properties_.find(symbol);
458 if (it == properties_.end()) {
459 return Domain::Complex;
460 }
461 return it->second.most_specific_domain;
462}
463
464std::unordered_set<Sign, SignHash> PropertyStore::get_signs(const std::string& symbol) const {
465 auto it = properties_.find(symbol);
466 if (it == properties_.end()) {
467 return {};
468 }
469 return it->second.signs;
470}
471
472Parity PropertyStore::get_parity(const std::string& symbol) const {
473 auto it = properties_.find(symbol);
474 if (it == properties_.end()) {
475 return Parity::Unknown;
476 }
477 return it->second.parity;
478}
479
480Boundedness PropertyStore::get_boundedness(const std::string& symbol) const {
481 auto it = properties_.find(symbol);
482 if (it == properties_.end()) {
484 }
485 return it->second.boundedness;
486}
487
488std::optional<Interval> PropertyStore::get_bounds(const std::string& symbol) const {
489 auto it = properties_.find(symbol);
490 if (it == properties_.end()) {
491 return std::nullopt;
492 }
493 return it->second.bounds;
494}
495
496bool PropertyStore::has_sign(const std::string& symbol, Sign sign) const {
497 auto it = properties_.find(symbol);
498 if (it == properties_.end()) {
499 return false;
500 }
501 return it->second.signs.count(sign) > 0;
502}
503
504bool PropertyStore::has_domain(const std::string& symbol, Domain domain) const {
505 auto it = properties_.find(symbol);
506 if (it == properties_.end()) {
507 // Default is Complex; only Complex query returns true
508 return domain == Domain::Complex;
509 }
510 // The symbol has domain D. It "has" domain X if X is an ancestor of D (or equal to D).
511 // i.e., the symbol's specificity is >= the queried domain's specificity.
512 return domain_specificity(it->second.most_specific_domain) >= domain_specificity(domain);
513}
514
515}
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(std::optional< Interval > value)
BoundsCandidateResult bounded_interval_candidate(const Interval &bounds, const std::optional< Interval > &existing, ComputationContext &context)
Result< std::optional< Interval > > BoundsCandidateResult
ComputationContext const std::string & operation
PropertyStoreResult checked_property_update(PropertyStore &store, const std::string &symbol, const std::string &operation, F &&update)
std::string domain_str(Domain domain)
PropertyStoreResult invalid_empty_symbol(const std::string &operation)
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
Result< void > PropertyStoreResult
Domain
Mathematical domain hierarchy (least specific → most specific). Complex ⊃ Real ⊃ Algebraic ⊃ Rational...
@ Complex
Complex numbers (default, least specific)
@ Rational
Rational numbers
@ Natural
Non-negative integers (0, 1, 2, ...)
@ Real
Real numbers
@ Algebraic
Algebraic numbers (roots of polynomials with rational coefficients)
@ PositiveInt
Positive integers (1, 2, 3, ...)
Boundedness
Boundedness classification for symbols.
@ Unknown
Boundedness not determined
@ Bounded
Symbol has finite bounds
const MultiPoly & b
__attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) std const VectorField const std::string const std::shared_ptr< SymbolicExpr > & a
const NumberDomainSet & domain
Parity
Integer parity classification.
@ Unknown
Parity not determined
Sign
Sign classification for symbols and expressions.
内部 AST 定义。