LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
facts_query.cpp
浏览该文件的文档.
7
8#include <new>
9#include <utility>
10
11namespace LMCAS::detail {
12std::optional<Rational> closed_rational_value(
13 const std::shared_ptr<const SymbolicNode>& node, Domain domain, ComputationContext& context);
14
15namespace {
16using Node = std::shared_ptr<const SymbolicNode>;
17using T = Tribool;
19
21 if (a == T::False || b == T::False) return T::False;
22 return a == T::True && b == T::True ? T::True : T::Unknown;
23}
24
26 if (a == T::True || b == T::True) return T::True;
27 return a == T::False && b == T::False ? T::False : T::Unknown;
28}
29
30struct ValueFacts {
31 T defined = T::Unknown;
32 T real = T::Unknown;
33 T nonzero = T::Unknown;
34 T positive = T::Unknown;
35 T nonnegative = T::Unknown;
36};
37
38// Traverse once without normalization, floating evaluation, or callbacks.
40public:
42 ComputationContext& context, bool project = false)
44 budget_(context, context.limits().max_recursion_depth,
45 context.limits().max_ast_nodes, "facts.domain_condition") {}
46
47 std::optional<CasError> error;
48 bool expressible = true;
50 std::vector<std::shared_ptr<SymbolicExpr>> conditions;
51
52 ValueFacts walk(const Node& node, bool values = false) {
53 if (error) return {};
54 if (!node) {
55 error = CasError{CasErrc::InvalidArgument, "null expression", "facts.domain"};
56 return {};
57 }
58 budget_.measure(node);
59 auto entered = context_.enter_recursion("facts.domain");
60 if (!entered) {
61 error = entered.error();
62 return {};
63 }
64 struct Leave {
66 ~Leave() { context.leave_recursion(); }
67 } leave{context_};
68 auto result = structural(node, values);
69 // Value queries need definedness; projections retain conditions such as x != 0.
70 if (result.defined == T::False || (result.defined != T::True && !project_)) {
71 result.real = result.nonzero = result.positive = result.nonnegative = T::Unknown;
72 } else if (result.defined == T::True && values) {
73 infer(node, result);
74 }
75 return result;
76 }
77
79
80private:
86 std::size_t condition_nodes_ = 0;
87
89 if (!result) {
90 if (!error) error = result.error();
91 return T::Unknown;
92 }
93 return result.value();
94 }
95
96 template<class N, class... Args>
97 Node make(Args&&... args) {
98 if (error) return {};
99 auto result = make_node<N>(std::forward<Args>(args)...);
100 budget_.measure(result);
101 return result;
102 }
103
104 Node number(int n) { return make<NumberNode>(BigInt(n)); }
106 return make<RelationalNode>(std::move(a), std::move(b), op);
107 }
109 return make<LogicalNode>(std::move(a), std::move(b), op);
110 }
111 Node function(FT type, const Node& argument) {
112 return make<FunctionNode>(type, std::vector<Node>{argument});
113 }
115 std::vector<Node> operands;
116 operands.reserve(2);
117 operands.push_back(std::move(a));
118 operands.push_back(std::move(b));
119 return make<MultiplyNode>(std::move(operands));
120 }
121
122 template<class Predicate>
123 void require(ValueFacts& result, T truth, Predicate&& predicate,
124 bool real_restriction = true) {
125 result.defined = conjunction(result.defined, truth);
126 if (domain_ == Domain::Real && real_restriction && truth == T::False)
127 { violated_real_restriction = true; }
128 if (project_ && truth == T::Unknown && !error) {
129 auto condition = predicate();
130 if (!error && condition) {
132 condition_nodes_, budget_.measure(condition));
133 conditions.push_back(make_expression_ptr(std::move(condition)));
134 }
135 }
136 }
137
138 static void implications(ValueFacts& result) {
139 if (result.nonnegative == T::True && result.real != T::False) {
140 result.real = T::True;
141 if (result.nonzero == T::True) result.positive = T::True;
142 }
143 if (result.real == T::False) {
144 result.positive = result.nonnegative = T::False;
145 } else if (result.positive == T::True) {
146 result.real = result.nonzero = result.nonnegative = T::True;
147 } else if (result.nonzero == T::False) {
148 result.real = result.nonnegative = T::True;
149 result.positive = T::False;
150 } else if (result.real == T::True && result.nonnegative == T::False) {
151 result.nonzero = T::True;
152 result.positive = T::False;
153 }
154 }
155
156 void infer(const Node& node, ValueFacts& result) {
157 implications(result);
158 if (result.positive == T::Unknown) { result.positive = checked(facts_.is_positive(node, context_)); }
159 if (error) { return; }
160 implications(result);
161 if (result.real == T::Unknown) { result.real = checked(facts_.is_real(node, context_)); }
162 if (error) { return; }
163 if (result.nonzero == T::Unknown) { result.nonzero = checked(facts_.is_nonzero(node, context_)); }
164 if (error) { return; }
165 if (result.nonnegative == T::Unknown) { result.nonnegative = checked(facts_.is_nonnegative(node, context_)); }
166 if (error) { return; }
167 if (result.positive == T::Unknown || result.nonnegative == T::Unknown) {
168 auto negative = checked(facts_.is_negative(node, context_));
169 if (error) { return; }
170 if (negative == T::True) {
171 result.real = result.nonzero = T::True;
172 result.positive = result.nonnegative = T::False;
173 }
174 }
175 implications(result);
176 }
177
179 T all_real = T::True, all_nonnegative = T::True, all_nonzero = T::True;
180 bool any_positive = false, any_zero = false, all_zero = true, signs_known = true;
181 unsigned negative_parity = 0;
182 std::size_t nonreal_count = 0;
184
185 ValueFacts finish(ValueFacts result, bool product) const {
186 if (all_real == T::True) { result.real = T::True; }
187 if (nonreal_count == 1 && classified_reality && (!product || other_factors_nonzero))
188 { result.real = T::False; }
189 if (product) {
190 if (any_zero) { result.nonzero = T::False; }
191 else if (all_nonzero == T::True) { result.nonzero = T::True; }
192 if (signs_known) {
193 result.positive = result.nonnegative = negative_parity ? T::False : T::True;
194 }
195 } else if (all_nonnegative == T::True) {
196 result.nonnegative = T::True;
197 if (any_positive) { result.positive = T::True; }
198 if (all_zero) { result.nonzero = T::False; }
199 }
200 return result;
201 }
202 };
203
204 ValueFacts arithmetic(const std::vector<Node>& operands, bool product, bool values) {
205 ValueFacts result;
206 result.defined = T::True;
207 ArithmeticReduction reduction;
208 for (const auto& operand : operands) {
209 auto child = walk(operand, values);
210 if (error) { return {}; }
211 result.defined = conjunction(result.defined, child.defined);
212 if (!values) { continue; }
213 reduction.all_real = conjunction(reduction.all_real, child.real);
214 reduction.all_nonnegative = conjunction(reduction.all_nonnegative, child.nonnegative);
215 reduction.all_nonzero = conjunction(reduction.all_nonzero, child.nonzero);
216 reduction.any_positive |= child.positive == T::True;
217 reduction.any_zero |= child.nonzero == T::False;
218 reduction.all_zero &= child.nonzero == T::False;
219 if (child.real == T::False) { ++reduction.nonreal_count; }
220 else if (child.real == T::Unknown) { reduction.classified_reality = false; }
221 else if (child.nonzero != T::True) { reduction.other_factors_nonzero = false; }
222 if (child.positive == T::True) {
223 continue;
224 } else if (child.real == T::True && child.nonnegative == T::False) {
225 reduction.negative_parity ^= 1;
226 } else {
227 reduction.signs_known = false;
228 }
229 }
230 if (!values) { return result; }
231 return reduction.finish(result, product);
232 }
233
234 ValueFacts power(const PowerNode& node, bool values) {
235 const auto exponent_number = std::dynamic_pointer_cast<const NumberNode>(node.exponent());
236 BigInt integer;
237 const bool integral = exponent_number && try_get_integer_value(exponent_number, integer);
238 bool needs_base_value = values || !integral;
239 if (!needs_base_value) {
240 needs_base_value = integer.is_negative() ||
241 (integer.is_zero() && domain_ == Domain::Real);
242 }
243 auto base = walk(node.base(), needs_base_value);
244 if (error) { return {}; }
245 auto exponent = walk(node.exponent(), !integral);
246 if (error) { return {}; }
247 ValueFacts result;
248 result.defined = conjunction(base.defined, exponent.defined);
249 if (integral) {
250 return integral_power(node, base, integer, result);
251 }
252 if (exponent_number) {
253 return nonintegral_power(node, base, exponent, result, true);
254 }
255 if (exponent.nonzero == T::False) {
256 return integral_power(node, base, BigInt(0), result);
257 }
258 const auto exact_exponent = closed_rational_value(node.exponent(), domain_, context_);
259 if (exact_exponent && exact_exponent->is_integer()) {
260 return integral_power(node, base, exact_exponent->to_bigint(), result);
261 }
262 return nonintegral_power(node, base, exponent, result, exact_exponent.has_value());
263 }
264
265 ValueFacts integral_power(const PowerNode& node, const ValueFacts& base, const BigInt& integer, ValueFacts result) {
266 const bool zero = integer == BigInt(0);
267 if (integer.is_negative() || (zero && domain_ == Domain::Real)) {
268 require(result, base.nonzero, [&] {
269 return relation(node.base(), number(0), RelationOp::NEQ);
270 });
271 }
272 if (zero) {
273 result.real = result.nonzero = result.positive = result.nonnegative = T::True;
274 } else {
275 if (base.real == T::True) { result.real = T::True; }
276 const T nonzero = project_ && integer.is_negative() ? T::True : base.nonzero;
277 result.nonzero = nonzero;
278 if (base.real == T::True) {
279 if (integer % BigInt(2) == BigInt(0)) {
280 result.nonnegative = T::True;
281 result.positive = nonzero;
282 } else {
283 result.positive = base.positive;
284 result.nonnegative = base.nonnegative;
285 }
286 }
287 }
288 return result;
289 }
290
291 T nonintegral_domain(const ValueFacts& base, const ValueFacts& exponent, bool known_noninteger, bool& unresolved_integer_domain) {
292 T allowed;
293 unresolved_integer_domain = false;
294 if (domain_ == Domain::Real) {
295 allowed = disjunction(conjunction(base.positive, exponent.real),
296 conjunction(base.nonnegative, exponent.positive));
297 unresolved_integer_domain = !known_noninteger && base.nonnegative != T::True;
298 if (unresolved_integer_domain && allowed != T::True) { allowed = T::Unknown; }
299 if (known_noninteger && base.real == T::True && base.nonnegative == T::False)
300 { allowed = T::False; }
301 if (base.nonzero == T::False && exponent.positive == T::False &&
302 exponent.real == T::True) { allowed = T::False; }
303 } else {
304 const T zero_nonnegative = conjunction(
305 base.nonzero == T::False ? T::True :
306 base.nonzero == T::True ? T::False : T::Unknown,
307 exponent.nonnegative);
308 allowed = disjunction(base.nonzero, zero_nonnegative);
309 }
310 return allowed;
311 }
312
313 ValueFacts nonintegral_power(const PowerNode& node, const ValueFacts& base, const ValueFacts& exponent, ValueFacts result, bool known_noninteger) {
314 bool unresolved_integer_domain;
315 const T allowed = nonintegral_domain(base, exponent, known_noninteger, unresolved_integer_domain);
316 if (allowed == T::Unknown &&
317 (exponent.real != T::True || unresolved_integer_domain)) {
318 expressible = false;
319 result.defined = conjunction(result.defined, T::Unknown);
320 } else {
321 require(result, allowed, [&] {
322 return logical(
323 relation(node.base(), number(0),
326 relation(node.exponent(), number(0),
330 });
331 }
332 return nonintegral_value(base, exponent, result, known_noninteger);
333 }
334
336 ValueFacts result, bool known_noninteger) const {
337 result.nonzero = base.nonzero;
338 if (base.nonzero == T::False && domain_ == Domain::Complex &&
339 exponent.positive != T::True) {
340 return zero_base_power_value(exponent, result);
341 }
342 if (base.positive == T::True && exponent.real == T::True) {
343 result.real = result.positive = result.nonnegative = T::True;
344 } else if (base.nonnegative == T::True && exponent.positive == T::True) {
345 result.real = result.nonnegative = T::True;
346 result.positive = base.nonzero;
347 } else if (known_noninteger && base.real == T::True && base.nonnegative == T::False) {
348 result.real = T::False;
349 }
350 return result;
351 }
352
354 result.nonzero = T::Unknown;
355 if (exponent.real != T::True ||
356 (exponent.nonnegative != T::True && !project_)) { return result; }
357 result.real = result.nonnegative = T::True;
358 if (project_ && exponent.positive == T::False) {
359 result.nonzero = result.positive = T::True;
360 }
361 return result;
362 }
363
364 ValueFacts elementary(const FunctionNode& node, bool values) {
365 const auto& arguments = node.arguments();
366 if (node.type() == FT::Infinity) { return {T::False}; }
367 const bool logarithm_base = node.type() == FT::Log && arguments.size() == 2;
368 if (arguments.size() != 1 && !logarithm_base) {
369 expressible = false;
370 return {};
371 }
372 const auto& argument = arguments[0];
373 const bool restricted = restricted_argument(node.type());
374 auto child = walk(argument, values || restricted);
375 if (error) { return {}; }
376 ValueFacts result;
377 result.defined = child.defined;
378 return elementary_value(node, child, result);
379 }
380
381 bool restricted_argument(FT type) const {
382 if (type == FT::Ln || type == FT::Log || type == FT::Tan ||
383 type == FT::Sec || type == FT::Cot || type == FT::Csc) {
384 return true;
385 }
386 if (type == FT::ArcTan || type == FT::Sgn) {
387 return domain_ == Domain::Complex;
388 }
389 if (type == FT::Sqrt || type == FT::ArcSin || type == FT::ArcCos) {
390 return domain_ == Domain::Real;
391 }
392 return false;
393 }
394
396 switch (node.type()) {
397 case FT::Exp: {
398 result.nonzero = T::True;
399 if (child.real == T::True)
400 { result.real = result.positive = result.nonnegative = T::True; }
401 return result;
402 }
403 case FT::Sin: { break; }
404 case FT::Cos: { break; }
405 case FT::Sinh: { break; }
406 case FT::Cosh: { break; }
407 case FT::Erf: { break; }
408 case FT::Si: { break; }
409 default: { return restricted_function(node, child, result); }
410 }
411 if (child.real == T::True) { result.real = T::True; }
412 if (child.nonzero == T::False) {
413 result.nonzero = node.type() == FT::Cos || node.type() == FT::Cosh
414 ? T::True : T::False;
415 if (result.nonzero == T::True) { result.positive = T::True; }
416 }
417 return result;
418 }
419
421 switch (node.type()) {
422 case FT::Ln: { return logarithm(node.arguments(), child, result); }
423 case FT::Log: { return logarithm(node.arguments(), child, result); }
424 case FT::Sqrt: { return real_valued_function(node, child, result); }
425 case FT::Abs: { return real_valued_function(node, child, result); }
426 case FT::ComplexAbs: { return real_valued_function(node, child, result); }
427 case FT::Tan: { return trigonometric_domain(node, child, result); }
428 case FT::Sec: { return trigonometric_domain(node, child, result); }
429 case FT::Cot: { return trigonometric_domain(node, child, result); }
430 case FT::Csc: { return trigonometric_domain(node, child, result); }
431 case FT::ArcSin: { return trigonometric_domain(node, child, result); }
432 case FT::ArcCos: { return trigonometric_domain(node, child, result); }
433 case FT::Sgn: { return real_valued_function(node, child, result); }
434 case FT::ArcTan: { return trigonometric_domain(node, child, result); }
435 case FT::LambertW: { return lambert_w_domain(node, child, result); }
436 default: {
437 // Unsupported singularities and conditional branches remain undecidable.
438 result.defined = conjunction(result.defined, T::Unknown);
439 expressible = false;
440 break;
441 }
442 }
443 return result;
444 }
445
447 if (node.type() == FT::Sqrt) {
448 if (domain_ == Domain::Real) {
449 require(result, child.nonnegative, [&] {
450 return relation(node.arguments()[0], number(0), RelationOp::GEQ);
451 });
452 }
453 result.nonzero = child.nonzero;
454 if (child.nonnegative == T::True || (project_ && domain_ == Domain::Real)) {
455 result.real = result.nonnegative = T::True;
456 result.positive = child.nonzero;
457 } else if (child.real == T::True && child.nonnegative == T::False) {
458 result.real = T::False;
459 }
460 return result;
461 }
462 if (node.type() == FT::Abs || node.type() == FT::ComplexAbs) {
463 result.real = result.nonnegative = T::True;
464 result.nonzero = result.positive = child.nonzero;
465 return result;
466 }
467 if (domain_ == Domain::Real || child.real == T::True) {
468 if (child.defined == T::True) { result.real = T::True; }
469 result.nonzero = child.nonzero;
470 result.positive = child.positive;
471 result.nonnegative = child.nonnegative;
472 } else {
473 result.defined = conjunction(result.defined, T::Unknown);
474 expressible = false;
475 }
476 return result;
477 }
479 ValueFacts result) {
480 if (child.real == T::True && child.nonnegative == T::True) {
481 result.real = result.nonnegative = T::True;
482 result.positive = child.positive;
483 result.nonzero = child.nonzero;
484 return result;
485 }
486 if (domain_ == Domain::Real) {
487 const T in_principal_domain = child.nonnegative == T::True ? T::True : T::Unknown;
488 require(result, in_principal_domain, [&] {
489 auto lower = multiply(number(-1), function(FT::Exp, number(-1)));
490 return relation(node.arguments()[0], std::move(lower), RelationOp::GEQ);
491 });
492 if (in_principal_domain == T::True || project_) {
493 result.real = T::True;
494 result.nonnegative = child.nonnegative;
495 result.positive = child.positive;
496 result.nonzero = child.nonzero;
497 }
498 return result;
499 }
500 result.defined = conjunction(result.defined, T::Unknown);
501 expressible = false;
502 return result;
503 }
504
505 ValueFacts logarithm(const std::vector<Node>& arguments, const ValueFacts& child, ValueFacts result) {
506 const auto& argument = arguments[0];
507 require(result, domain_ == Domain::Real ? child.positive : child.nonzero, [&] {
508 return relation(argument, number(0),
509 domain_ == Domain::Real ? RelationOp::GT : RelationOp::NEQ);
510 });
511 if (child.positive == T::True || (project_ && domain_ == Domain::Real))
512 { result.real = T::True; }
513 if (const auto* numeric = dynamic_cast<const NumberNode*>(argument.get())) {
514 if (numeric->is_one()) { result.nonzero = T::False; }
515 else if (numeric->is_positive()) {
516 result.nonzero = T::True;
517 result.positive = result.nonnegative =
518 exact_number_as_rational(*numeric) > Rational(1) ? T::True : T::False;
519 } else if (!numeric->is_zero()) {
520 result.real = T::False;
521 result.nonzero = T::True;
522 }
523 }
524 if (arguments.size() == 2) { return logarithm_base(arguments[1], child, result); }
525 return result;
526 }
527 ValueFacts logarithm_base(const Node& argument, const ValueFacts& child, ValueFacts result) {
528 auto base = walk(argument, true);
529 if (error) { return {}; }
530 result.defined = conjunction(result.defined, base.defined);
531 require(result, domain_ == Domain::Real ? base.positive : base.nonzero, [&] {
532 return relation(argument, number(0),
533 domain_ == Domain::Real ? RelationOp::GT : RelationOp::NEQ);
534 });
535 T different_from_one = T::Unknown;
536 if (const auto* numeric = dynamic_cast<const NumberNode*>(argument.get()))
537 { different_from_one = numeric->is_one() ? T::False : T::True; }
538 require(result, different_from_one, [&] {
539 return relation(argument, number(1), RelationOp::NEQ);
540 });
541 result.real = (project_ && domain_ == Domain::Real) ||
542 (child.positive == T::True && base.positive == T::True)
543 ? T::True : T::Unknown;
544 result.positive = result.nonnegative = T::Unknown;
545 return result;
546 }
547
549 const auto& argument = node.arguments()[0];
550 if (node.type() == FT::ArcSin || node.type() == FT::ArcCos) {
551 if (domain_ == Domain::Real) {
552 T inside = T::Unknown;
553 if (const auto* numeric = dynamic_cast<const NumberNode*>(argument.get())) {
554 const auto value = exact_number_as_rational(*numeric);
555 inside = value >= Rational(-1) && value <= Rational(1) ? T::True : T::False;
556 }
557 require(result, inside, [&] {
558 return logical(relation(argument, number(-1), RelationOp::GEQ),
559 relation(argument, number(1), RelationOp::LEQ),
561 });
562 }
563 return result;
564 }
565 if (node.type() == FT::ArcTan) {
566 if (child.real == T::True) {
567 result.real = T::True;
568 result.nonzero = child.nonzero;
569 result.positive = child.positive;
570 result.nonnegative = child.nonnegative;
571 } else if (domain_ == Domain::Complex) {
572 result.defined = conjunction(result.defined, T::Unknown);
573 expressible = false;
574 }
575 return result;
576 }
577 const bool cosine = node.type() == FT::Tan || node.type() == FT::Sec;
578 T nonzero = T::Unknown;
579 if (child.nonzero == T::False) { nonzero = cosine ? T::True : T::False; }
580 require(result, nonzero, [&] {
581 return relation(function(cosine ? FT::Cos : FT::Sin, argument),
583 });
584 if (child.real == T::True) { result.real = T::True; }
585 return result;
586 }
587
588 ValueFacts structural(const Node& node, bool values) {
589 if (const auto* number = dynamic_cast<const NumberNode*>(node.get())) {
590 const T nz = number->is_zero() ? T::False : T::True;
591 const T positive = number->is_positive() ? T::True : T::False;
592 return {T::True, T::True, nz, positive,
593 nz == T::False || positive == T::True ? T::True : T::False};
594 }
595 if (const auto* variable = dynamic_cast<const VariableNode*>(node.get())) {
596 return variable_facts(*variable);
597 }
598 if (const auto* add = dynamic_cast<const AddNode*>(node.get()))
599 { return arithmetic(add->operands(), false, values); }
600 if (const auto* multiply = dynamic_cast<const MultiplyNode*>(node.get()))
601 { return arithmetic(multiply->operands(), true, values); }
602 if (const auto* pow = dynamic_cast<const PowerNode*>(node.get())) { return power(*pow, values); }
603 if (const auto* fn = dynamic_cast<const FunctionNode*>(node.get())) { return elementary(*fn, values); }
604 if (const auto* complex = dynamic_cast<const ComplexNode*>(node.get())) {
605 return complex_facts(*complex);
606 }
607 if (const auto* root = dynamic_cast<const RootOfNode*>(node.get())) {
608 return root_facts(*root, values);
609 }
610 if (const auto* relation = dynamic_cast<const RelationalNode*>(node.get())) {
611 auto left = walk(relation->left());
612 if (error) { return {}; }
613 auto right = walk(relation->right());
614 return {conjunction(left.defined, right.defined)};
615 }
616 expressible = false;
617 return {};
618 }
619
621 if (variable.is_constant() &&
622 (variable.name() == "pi" || variable.name() == "π" ||
623 variable.name() == "e" || variable.name() == "phi"))
624 { return {T::True, T::True, T::True, T::True, T::True}; }
625 return {T::True};
626 }
627
629 auto real = walk(complex.real(), true);
630 if (error) { return {}; }
631 auto imag = walk(complex.imag(), true);
632 if (error) { return {}; }
633 ValueFacts result;
634 result.defined = conjunction(conjunction(real.defined, imag.defined),
635 conjunction(real.real, imag.real));
636 if (real.real == T::Unknown || imag.real == T::Unknown) { expressible = false; }
637 if (domain_ == Domain::Real) {
638 const T zero = imag.nonzero == T::False ? T::True :
639 imag.nonzero == T::True ? T::False : T::Unknown;
640 require(result, zero, [&] {
641 return relation(complex.imag(), number(0), RelationOp::EQ);
642 }, false);
643 }
644 result.nonzero = disjunction(real.nonzero, imag.nonzero);
645 result.real = imag.nonzero == T::False ? T::True :
646 imag.nonzero == T::True ? T::False : T::Unknown;
647 if (result.real == T::True) {
648 result.positive = real.positive;
649 result.nonnegative = real.nonnegative;
650 }
651 return result;
652 }
653
654 ValueFacts root_facts(const RootOfNode& root, bool values) {
655 ValueFacts result;
656 result.defined = T::True; /**< ExactRootId 已在构造时校验。 */
657 if (values || domain_ == Domain::Real) {
658 auto isolation = isolate_exact_root(root.exact_id(), context_, "facts.rootof");
659 if (!isolation) {
660 error = isolation.error();
661 return {};
662 }
663 if (const auto* real = std::get_if<RealIsolation>(&isolation.value())) {
664 result.real = T::True;
665 if (real->value.lower > Rational(0)) { result.positive = T::True; }
666 else if (real->value.upper < Rational(0)) { result.nonnegative = T::False; }
667 else if (real->value.lower == Rational(0) && real->value.upper == Rational(0))
668 { result.nonzero = T::False; }
669 } else {
670 result.real = T::False;
671 result.nonzero = T::True;
672 if (domain_ == Domain::Real) { result.defined = T::False; }
673 }
674 }
675 return result;
676 }
677};
678
681 bool* violated_real_restriction = nullptr) {
684 "value domain must be Real or Complex", "facts.domain");
685 }
686 try {
687 DomainTraversal traversal(facts, domain, context);
688 auto result = traversal.walk(node, values);
689 if (traversal.error) return Result<T>::failure(*traversal.error);
690 if (violated_real_restriction)
691 { *violated_real_restriction = traversal.violated_real_restriction; }
692 return result.*member;
693 } catch (const CasError& error) {
694 return Result<T>::failure(error);
695 } catch (const std::bad_alloc&) {
697 "value facts allocation failed", "facts.domain");
698 }
699}
700
701}
702
704 ComputationContext& context, bool* violated_real_restriction) {
705 return query_value(node, facts, domain, &ValueFacts::defined, false, context,
706 violated_real_restriction);
707}
709 return query_value(node, facts, domain, &ValueFacts::nonzero, true, context);
710}
712 return query_value(node, facts, Domain::Complex, &ValueFacts::real, true, context);
713}
715 return query_value(node, facts, Domain::Complex, &ValueFacts::positive, true, context);
716}
718 return query_value(node, facts, Domain::Complex, &ValueFacts::nonnegative, true, context);
719}
720
723 using Output = std::optional<std::vector<std::shared_ptr<SymbolicExpr>>>;
726 "value domain must be Real or Complex", "facts.domain");
727 }
728 try {
729 DomainTraversal traversal(facts, domain, context, true);
730 auto result = traversal.walk(node);
731 if (traversal.error) return Result<Output>::failure(*traversal.error);
732 if (result.defined == T::False) {
733 if (domain == Domain::Real && !traversal.violated_real_restriction) {
734 auto complex_defined = query_definedness(node, facts, Domain::Complex, context);
735 if (!complex_defined) return Result<Output>::failure(complex_defined.error());
736 if (complex_defined.value() != T::False) {
737 auto real_value = query_real_value(node, facts, context);
738 if (!real_value) return Result<Output>::failure(real_value.error());
739 if (real_value.value() == T::Unknown) return Output{};
740 }
741 }
742 auto impossible = traversal.false_condition();
743 if (traversal.error) return Result<Output>::failure(*traversal.error);
744 traversal.conditions.clear();
745 traversal.conditions.push_back(make_expression_ptr(std::move(impossible)));
746 } else if (!traversal.expressible) {
747 return Output{};
748 }
749 return Output{std::move(traversal.conditions)};
750 } catch (const CasError& error) {
751 return Result<Output>::failure(error);
752 } catch (const std::bad_alloc&) {
754 "domain projection allocation failed", "facts.domain");
755 }
756}
757
758}
加法节点,表示多个操作数的求和。
复数节点,表示 real + imag * i。
virtual Result< Tribool > is_real(const std::shared_ptr< const SymbolicNode > &node, ComputationContext &context) const =0
virtual Result< Tribool > is_nonnegative(const std::shared_ptr< const SymbolicNode > &node, ComputationContext &context) const =0
virtual Result< Tribool > is_positive(const std::shared_ptr< const SymbolicNode > &node, ComputationContext &context) const =0
virtual Result< Tribool > is_nonzero(const std::shared_ptr< const SymbolicNode > &node, ComputationContext &context) const =0
virtual Result< Tribool > is_negative(const std::shared_ptr< const SymbolicNode > &node, ComputationContext &context) const =0
函数节点,表示数学函数调用(三角函数、对数、特殊函数等)。
乘法节点,表示多个操作数的乘积。
幂运算节点,表示 base^exponent。
CasError & error()
static Result failure(CasErrc code, std::string message, std::string operation={})
const LMCAS::detail::ExactRootId & exact_id() const noexcept
变量节点,表示一个符号变量。
std::size_t measure(const std::shared_ptr< const SymbolicNode > &node)
std::size_t append_size(std::size_t current, std::size_t child)
ValueFacts elementary_value(const FunctionNode &node, const ValueFacts &child, ValueFacts result)
void require(ValueFacts &result, T truth, Predicate &&predicate, bool real_restriction=true)
ValueFacts elementary(const FunctionNode &node, bool values)
T nonintegral_domain(const ValueFacts &base, const ValueFacts &exponent, bool known_noninteger, bool &unresolved_integer_domain)
ValueFacts restricted_function(const FunctionNode &node, const ValueFacts &child, ValueFacts result)
ValueFacts logarithm(const std::vector< Node > &arguments, const ValueFacts &child, ValueFacts result)
ValueFacts lambert_w_domain(const FunctionNode &node, const ValueFacts &child, ValueFacts result)
ValueFacts nonintegral_value(const ValueFacts &base, const ValueFacts &exponent, ValueFacts result, bool known_noninteger) const
ValueFacts trigonometric_domain(const FunctionNode &node, const ValueFacts &child, ValueFacts result)
ValueFacts logarithm_base(const Node &argument, const ValueFacts &child, ValueFacts result)
ValueFacts zero_base_power_value(const ValueFacts &exponent, ValueFacts result) const
DomainTraversal(const FactsQuery &facts, Domain domain, ComputationContext &context, bool project=false)
ValueFacts arithmetic(const std::vector< Node > &operands, bool product, bool values)
ValueFacts integral_power(const PowerNode &node, const ValueFacts &base, const BigInt &integer, ValueFacts result)
ValueFacts nonintegral_power(const PowerNode &node, const ValueFacts &base, const ValueFacts &exponent, ValueFacts result, bool known_noninteger)
ValueFacts real_valued_function(const FunctionNode &node, const ValueFacts &child, ValueFacts result)
Result< Tribool > query_value(const Node &node, const FactsQuery &facts, Domain domain, T ValueFacts::*member, bool values, ComputationContext &context, bool *violated_real_restriction=nullptr)
多元 Hensel 提升(单变量步)
std::shared_ptr<::LMCAS::SymbolicExpr > make_expression_ptr(SymbolicNodePtr root)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
Result< RootIsolation > isolate_exact_root(const ExactRootId &root, ComputationContext &context, const std::string &operation)
Result< Tribool > query_nonzero_value(const Node &node, const FactsQuery &facts, Domain domain, ComputationContext &context)
const std::string & variable
std::shared_ptr< const SymbolicNode > Node
RewriteBudget const FactsQuery Domain domain
Result< Tribool > query_definedness(const Node &node, const FactsQuery &facts, Domain domain, ComputationContext &context, bool *violated_real_restriction)
RewriteBudget const FactsQuery & facts
std::optional< Rational > closed_rational_value(const std::shared_ptr< const SymbolicNode > &node, Domain domain, ComputationContext &context)
std::shared_ptr< const Node > make_node(Args &&... args)
Result< std::optional< std::vector< std::shared_ptr< SymbolicExpr > > > > domain_constraints(const Node &node, const FactsQuery &facts, Domain domain, ComputationContext &context)
Result< Tribool > query_real_value(const Node &node, const FactsQuery &facts, ComputationContext &context)
Result< Tribool > query_nonnegative_value(const Node &node, const FactsQuery &facts, ComputationContext &context)
std::size_t std::size_t ComputationContext & context
Result< Tribool > query_positive_value(const Node &node, const FactsQuery &facts, ComputationContext &context)
ExprResult pow(const ExprPtr &base, const ExprPtr &exponent, ComputationContext &context)
ExprResult integer(Integer)=delete
bool try_get_integer_value(const std::shared_ptr< const NumberNode > &node, BigInt &value)
ExprResult real(const ExprPtr &expression, ComputationContext &context)
const ExprPtr & lower
Domain
Mathematical domain hierarchy (least specific → most specific). Complex ⊃ Real ⊃ Algebraic ⊃ Rational...
@ Complex
Complex numbers (default, least specific)
@ Real
Real numbers
Tribool
Tri-state logic value for property queries.
ExprResult add(const ExprPtr &lhs, const ExprPtr &rhs, ComputationContext &context)
const MultiPoly & b
RelationOp
Stable relational operator used by public APIs.
const ExprPtr RelationOp op
ExprResult complex(ExprPtr real, ExprPtr imag)
const ExprPtr & exponent
__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
ExprPtr imag
ExprResult member(const ExprPtr &element, const ExprPtr &set, ComputationContext &context)
const ExprPtr & right
const std::string const ExprPtr & value
Rational exact_number_as_rational(const NumberNode &node)
内部 AST 定义。