LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
inference_engine_bounds.cpp
浏览该文件的文档.
6#include <array>
7#include <limits>
8#include <utility>
9
10namespace LMCAS {
11
12
13namespace {
14constexpr const char* bounds_operation = "inference.bounds";
15using RationalBounds = std::pair<Rational, Rational>;
16
19 "a finite real enclosure is not proven", bounds_operation);
20}
21
22Interval closed_bounds(const Rational& lower, const Rational& upper) {
23 return Interval{Endpoint::closed(SymbolicExpr::number(lower)),
24 Endpoint::closed(SymbolicExpr::number(upper))};
25}
27public:
28 BoundsPropagation(const AssumptionContext& assumptions, const InferenceEngine& engine)
29 : assumptions_(assumptions), facts_(engine) {}
30
31 Result<Interval> propagate(const std::shared_ptr<const SymbolicNode>& node) {
32 auto entered = context_.enter_recursion(bounds_operation);
33 if (!entered) { return Result<Interval>::failure(entered.error()); }
34 struct Leave {
36 ~Leave() { context.leave_recursion(); }
37 } leave{context_};
38 if (!node) { return unknown_bounds(); }
39 if (dynamic_cast<const NumberNode*>(node.get())) {
41 Interval::point(detail::make_expression_ptr(node)));
42 }
43 if (const auto* variable = dynamic_cast<const VariableNode*>(node.get())) {
45 }
46 if (const auto* add = dynamic_cast<const AddNode*>(node.get())) {
47 return sum_bounds(*add);
48 }
49 if (const auto* multiply = dynamic_cast<const MultiplyNode*>(node.get())) {
50 return product_bounds(*multiply);
51 }
52 if (const auto* power = dynamic_cast<const PowerNode*>(node.get())) {
53 return power_bounds(*power);
54 }
55 if (const auto* function = dynamic_cast<const FunctionNode*>(node.get()))
56 { return function_bounds(*function); }
57 return unknown_bounds();
58 }
59
60private:
62 if (variable.is_constant()) { return unknown_bounds(); }
63 auto interval = assumptions_.get_bounds(variable.name());
64 if (!interval) { return unknown_bounds(); }
66 if (!lower) { return Result<Interval>::failure(lower.error()); }
68 if (!upper) { return Result<Interval>::failure(upper.error()); }
70 lower.value(), upper.value(), context_);
71 if (!order) { return Result<Interval>::failure(order.error()); }
72 if (order.value() > 0 || (order.value() == 0 &&
73 (interval->lower.is_open || interval->upper.is_open))) { return unknown_bounds(); }
74 return Result<Interval>::success(std::move(*interval));
75 }
76
78 RationalBounds total{Rational(0), Rational(0)};
79 for (const auto& operand : add.operands()) {
80 auto interval = propagate(operand);
81 if (!interval) { return interval; }
82 auto bounds = rational_bounds(interval.value());
83 if (!bounds) { return Result<Interval>::failure(bounds.error()); }
84 auto lower = arithmetic(total.first, bounds.value().first, '+');
85 if (!lower) { return Result<Interval>::failure(lower.error()); }
86 auto upper = arithmetic(total.second, bounds.value().second, '+');
87 if (!upper) { return Result<Interval>::failure(upper.error()); }
88 total = {std::move(lower.value()), std::move(upper.value())};
89 }
90 return Result<Interval>::success(closed_bounds(total.first, total.second));
91 }
92
94 std::optional<Interval> numerator;
95 std::optional<Interval> denominator;
96 for (const auto& operand : multiply.operands()) {
97 const auto* power = dynamic_cast<const PowerNode*>(operand.get());
98 const auto exponent = power
99 ? std::dynamic_pointer_cast<const NumberNode>(
100 power->exponent())
101 : nullptr;
102 const bool inverse = exponent && exponent->is_negative_one();
103 auto next = propagate(power && inverse ? power->base() : operand);
104 if (!next) { return next; }
105 auto& accumulated = inverse ? denominator : numerator;
106 if (!accumulated) {
107 accumulated = std::move(next.value());
108 } else {
109 auto product = multiply_intervals(*accumulated, next.value(), false);
110 if (!product) { return product; }
111 accumulated = std::move(product.value());
112 }
113 }
114 if (!numerator) { numerator = closed_bounds(Rational(1), Rational(1)); }
115 if (!denominator) { return Result<Interval>::success(std::move(*numerator)); }
116 return multiply_intervals(*numerator, *denominator, true);
117 }
118
120 const auto exponent =
121 std::dynamic_pointer_cast<const NumberNode>(power.exponent());
122 BigInt integer_exponent;
123 if (!try_get_integer_value(exponent, integer_exponent)) {
124 return unknown_bounds();
125 }
126 const bool square = integer_exponent == BigInt(2);
127 const bool inverse = exponent->is_negative_one();
128 if (!square && !inverse) { return unknown_bounds(); }
129 auto base = propagate(power.base());
130 if (!base) { return base; }
131 if (inverse) { return multiply_intervals(closed_bounds(Rational(1), Rational(1)), base.value(), true); }
132 auto bounds = rational_bounds(base.value());
133 if (!bounds) { return Result<Interval>::failure(bounds.error()); }
134 auto a = arithmetic(bounds.value().first, bounds.value().first, '*');
135 if (!a) { return Result<Interval>::failure(a.error()); }
136 auto b = arithmetic(bounds.value().second, bounds.value().second, '*');
137 if (!b) { return Result<Interval>::failure(b.error()); }
138 if (bounds.value().first >= Rational(0))
139 { return Result<Interval>::success(closed_bounds(a.value(), b.value())); }
140 if (bounds.value().second <= Rational(0))
141 { return Result<Interval>::success(closed_bounds(b.value(), a.value())); }
142 auto comparison = arithmetic_budget(a.value(), b.value());
143 if (!comparison) { return Result<Interval>::failure(comparison.error()); }
144 return Result<Interval>::success(closed_bounds(Rational(0), std::max(a.value(), b.value())));
145 }
146
147 Result<void> arithmetic_budget(const Rational& a, const Rational& b) {
148 auto step = context_.consume_steps(1, bounds_operation);
149 if (!step) return step;
150 const std::size_t a_bits = std::max(a.get_numerator().bit_length(), a.get_denominator().bit_length());
151 const std::size_t b_bits = std::max(b.get_numerator().bit_length(), b.get_denominator().bit_length());
152 if (a_bits >= std::numeric_limits<std::size_t>::max() - b_bits)
153 return Result<void>::failure(CasErrc::ResourceLimit, "integer growth exceeds budget", bounds_operation);
154 return context_.require_integer_bits(a_bits + b_bits + 1, bounds_operation);
155 }
156
157 Result<Rational> arithmetic(const Rational& a, const Rational& b, char operation) {
158 auto growth = arithmetic_budget(a, b);
159 if (!growth) return Result<Rational>::failure(growth.error());
160 if (operation == '+') return Result<Rational>::success(a + b);
161 if (operation == '/') return Result<Rational>::success(a / b);
162 return Result<Rational>::success(a * b);
163 }
164
165 Result<Rational> rational_endpoint_bound(const Endpoint& endpoint, bool lower,
166 bool separate_zero = false) {
167 auto comparable = detail::comparable_endpoint(endpoint, context_, bounds_operation);
168 if (!comparable) { return Result<Rational>::failure(comparable.error()); }
169 auto& value = comparable.value();
171 "unbounded endpoint has no finite rational enclosure", bounds_operation); }
172 if (!value.algebraic) { return Result<Rational>::success(std::move(value.rational)); }
173 auto& algebraic = *value.algebraic;
174 if (separate_zero) {
175 auto sign = detail::compare_comparable(
177 if (!sign) { return Result<Rational>::failure(sign.error()); }
178 if (sign.value() == 0) { return Result<Rational>::success(Rational(0)); }
179 while (sign.value() > 0 ? algebraic.lower <= Rational(0) : algebraic.upper >= Rational(0)) {
181 if (!refined) { return Result<Rational>::failure(refined.error()); }
182 }
183 }
184 return Result<Rational>::success(lower ? algebraic.lower : algebraic.upper);
185 }
186
187 Result<RationalBounds> rational_bounds(const Interval& interval, bool separate_zero = false) {
188 auto lower = rational_endpoint_bound(interval.lower, true, separate_zero);
189 if (!lower) return Result<RationalBounds>::failure(lower.error());
190 auto upper = rational_endpoint_bound(interval.upper, false, separate_zero);
192 return Result<RationalBounds>::success({std::move(lower.value()), std::move(upper.value())});
193 }
194
195 Result<Interval> multiply_intervals(const Interval& left, const Interval& right, bool divide) {
196 auto lhs = rational_bounds(left);
197 if (!lhs) { return Result<Interval>::failure(lhs.error()); }
198 auto rhs = rational_bounds(right, divide);
199 if (!rhs) { return Result<Interval>::failure(rhs.error()); }
200 if (divide && rhs.value().first <= Rational(0) && rhs.value().second >= Rational(0))
201 { return unknown_bounds(); }
202 std::array<Rational, 4> corners;
203 std::size_t index = 0;
204 for (const Rational* a : {&lhs.value().first, &lhs.value().second}) {
205 for (const Rational* b : {&rhs.value().first, &rhs.value().second}) {
206 auto corner = arithmetic(*a, *b, divide ? '/' : '*');
207 if (!corner) { return Result<Interval>::failure(corner.error()); }
208 corners[index++] = std::move(corner.value());
209 }
210 }
211 for (const auto& corner : corners) {
212 auto checked = arithmetic_budget(corner, corner);
213 if (!checked) { return Result<Interval>::failure(checked.error()); }
214 }
215 auto extrema = std::minmax_element(corners.begin(), corners.end());
216 return Result<Interval>::success(closed_bounds(*extrema.first, *extrema.second));
217 }
218
219 Result<Endpoint> arctangent_endpoint(const Endpoint& endpoint, bool lower) {
220 if (lower ? endpoint.is_neg_infinity : endpoint.is_pos_infinity) {
221 if (!pi_bounds_) {
223 if (!pi) { return Result<Endpoint>::failure(pi.error()); }
224 pi_bounds_ = std::move(pi.value());
225 }
226 auto half = arithmetic(pi_bounds_->second, Rational(lower ? -2 : 2), '/');
227 if (!half) { return Result<Endpoint>::failure(half.error()); }
228 return Result<Endpoint>::success(Endpoint::open(SymbolicExpr::number(half.value())));
229 }
230 auto input = rational_endpoint_bound(endpoint, lower);
231 if (!input) { return Result<Endpoint>::failure(input.error()); }
232 auto bounds = detail::exact_atan_bounds(input.value(), 128, context_);
233 if (!bounds) { return Result<Endpoint>::failure(bounds.error()); }
234 auto value = SymbolicExpr::number(lower ? bounds.value().first : bounds.value().second);
235 return Result<Endpoint>::success(endpoint.is_open ? Endpoint::open(value) : Endpoint::closed(value));
236 }
237
239 if (function.arguments().size() != 1) { return unknown_bounds(); }
243 const auto& argument = function.arguments()[0];
244 auto defined = detail::query_definedness(argument, facts_, Domain::Real, context_);
245 if (!defined) { return Result<Interval>::failure(defined.error()); }
246 if (defined.value() != Tribool::True) { return unknown_bounds(); }
247 auto argument_bounds = propagate(argument);
248 if (!argument_bounds && argument_bounds.error().code != CasErrc::Inconclusive)
249 { return argument_bounds; }
250 if (!argument_bounds) {
251 auto real = detail::query_real_value(argument, facts_, context_);
252 if (!real) { return Result<Interval>::failure(real.error()); }
253 if (real.value() != Tribool::True) { return unknown_bounds(); }
254 }
256 { return Result<Interval>::success(closed_bounds(Rational(-1), Rational(1))); }
257 return arctangent_bounds(argument_bounds);
258 }
259
261 Interval input = argument_bounds ? std::move(argument_bounds.value())
262 : Interval{Endpoint::neg_inf(), Endpoint::pos_inf()};
263 auto lower = arctangent_endpoint(input.lower, true);
264 if (!lower) { return Result<Interval>::failure(lower.error()); }
265 auto upper = arctangent_endpoint(input.upper, false);
266 if (!upper) { return Result<Interval>::failure(upper.error()); }
267 return Result<Interval>::success(Interval{std::move(lower.value()), std::move(upper.value())});
268 }
269
270 const AssumptionContext& assumptions_;
273 std::optional<detail::ExactConstantInterval> pi_bounds_;
274};
275}
276
277std::optional<Interval> InferenceEngine::propagate_bounds(const SymbolicExpr& expr) const {
278 try {
279 BoundsPropagation propagation(impl_->ctx, *this);
280 auto result = propagation.propagate(detail::node(expr));
281 if (result) return std::move(result.value());
282 } catch (const CasError&) {
283 return std::nullopt;
284 } catch (const std::bad_alloc&) {
285 return std::nullopt;
286 } catch (const std::length_error&) {
287 return std::nullopt;
288 }
289 return std::nullopt;
290}
291
292}
加法节点,表示多个操作数的求和。
函数节点,表示数学函数调用(三角函数、对数、特殊函数等)。
乘法节点,表示多个操作数的乘积。
幂运算节点,表示 base^exponent。
const std::shared_ptr< const SymbolicNode > & exponent() const noexcept
const std::shared_ptr< const SymbolicNode > & base() const noexcept
CasError & error()
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(T value)
变量节点,表示一个符号变量。
Result< Rational > rational_endpoint_bound(const Endpoint &endpoint, bool lower, bool separate_zero=false)
Result< RationalBounds > rational_bounds(const Interval &interval, bool separate_zero=false)
Result< Rational > arithmetic(const Rational &a, const Rational &b, char operation)
BoundsPropagation(const AssumptionContext &assumptions, const InferenceEngine &engine)
Result< Interval > propagate(const std::shared_ptr< const SymbolicNode > &node)
Result< Interval > multiply_intervals(const Interval &left, const Interval &right, bool divide)
Interval closed_bounds(const Rational &lower, const Rational &upper)
Result< ComparableEndpoint > comparable_endpoint(const Endpoint &endpoint, ComputationContext &context, const std::string &operation)
std::shared_ptr<::LMCAS::SymbolicExpr > make_expression_ptr(SymbolicNodePtr root)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
Result< Tribool > query_definedness(const Node &node, const FactsQuery &facts, Domain domain, ComputationContext &context, bool *violated_real_restriction)
Result< void > refine_exact_real_algebraic(ExactRealAlgebraic &value, ComputationContext &context, const std::string &operation)
Result< ExactConstantInterval > exact_atan_bounds(const Rational &value, std::size_t precision_bits, ComputationContext &context)
Result< ExactConstantInterval > exact_pi_bounds(std::size_t precision_bits, ComputationContext &context)
Result< Tribool > query_real_value(const Node &node, const FactsQuery &facts, ComputationContext &context)
Result< int > compare_comparable(const ComparableEndpoint &left, const ComparableEndpoint &right, ComputationContext &context)
const std::string & variable
__attribute__((visibility("default"))) ExprResult function(const std __attribute__((visibility("default"))) ExprResult finite_set(std ExprPtr upper
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
ExprResult interval(ExprPtr lower, ExprPtr upper, bool lower_closed, bool upper_closed)
bool try_get_integer_value(const std::shared_ptr< const NumberNode > &node, BigInt &value)
ExprResult real(const ExprPtr &expression, ComputationContext &context)
const ExprPtr & lower
const std::vector< FGLMPoly > const MonomialOrder & order
@ Real
Real numbers
std::size_t std::size_t std::size_t growth
@ True
Property definitely holds
ExprResult add(const ExprPtr &lhs, const ExprPtr &rhs, ComputationContext &context)
const MultiPoly & b
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
const ExprPtr & right
ExprResult pi()
const std::string const ExprPtr & value
const ExprPtr & rhs
const ExprPtr & denominator
ExprResult function(const std::string &name, std::vector< ExprPtr > arguments)