LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
rewrite_budget.cpp
浏览该文件的文档.
3
4#include <algorithm>
5#include <limits>
6
7namespace LMCAS::detail {
8namespace {
9
10constexpr const char* kDepthMessage = "equivalence rewrite depth budget exhausted";
11constexpr const char* kGrowthMessage = "equivalence node growth budget exhausted";
12constexpr const char* kStepMessage = "equivalence rewrite step budget exhausted";
13
15public:
17 : context_(context) {
18 auto entered = context_.enter_recursion(operation);
19 if (!entered) { throw entered.error(); }
20 }
21 ~MeasurementRecursion() { context_.leave_recursion(); }
24private:
26};
27}
28
30public:
31 // A fresh visitor handles each node; MeasurementRecursion checks the full path.
32 Measurement(RewriteBudget& budget, std::size_t path_depth, std::size_t active_depth)
33 : budget_(budget), path_depth_(path_depth), active_depth_(active_depth) {}
34
35 Shape shape{1, 1};
36
37 void visit(const NumberNode& number) override {
38 if (const auto* integer = std::get_if<BigInt>(&number.value())) {
40 } else {
41 const auto& rational = std::get<Rational>(number.value());
42 require_bits(rational.get_numerator());
43 require_bits(rational.get_denominator());
44 }
45 }
46
47protected:
48 void visit_child(const SymbolicNodePtr& child) override {
49 const auto measured = budget_.measure_shape(child, path_depth_ + 1, active_depth_);
50 shape.nodes = budget_.append_size(shape.nodes, measured.nodes);
51 if (measured.height == std::numeric_limits<std::size_t>::max()) {
53 }
54 shape.height = std::max(shape.height, measured.height + 1);
55 }
56
57private:
58 void require_bits(const BigInt& integer) {
60 integer.bit_length(), budget_.operation_);
61 if (!checked) { throw checked.error(); }
62 }
63
65 const std::size_t path_depth_;
66 const std::size_t active_depth_;
67};
68
69
71 std::size_t max_nodes, const char* operation)
72 : context_(context), remaining_steps_(std::numeric_limits<std::size_t>::max()),
73 max_depth_(max_depth), max_nodes_(std::min(max_nodes, context.limits().max_ast_nodes)),
74 limit_steps_(false),
76
78 std::size_t max_depth, std::size_t max_nodes,
79 const char* operation)
80 : context_(context), remaining_steps_(max_steps), max_depth_(max_depth),
81 max_nodes_(std::min(max_nodes, context.limits().max_ast_nodes)),
83
85 auto access = context_.consume_steps(0, operation_);
86 if (!access) { throw access.error(); }
87 if (limit_steps_ && remaining_steps_ == 0) {
88 throw CasError{CasErrc::ResourceLimit, kStepMessage, operation_};
89 }
90}
91
93 auto step = context_.consume_steps(1, operation_);
94 if (!step) { throw step.error(); }
96}
97
102
105 if (depth_ >= max_depth_) {
106 throw CasError{CasErrc::ResourceLimit, kDepthMessage, operation_};
107 }
108 auto entered = context_.enter_recursion(operation_);
109 if (!entered) { throw entered.error(); }
111 ++depth_;
112}
113
114void RewriteBudget::leave() noexcept {
115 --depth_;
117}
118
119void RewriteBudget::require_nodes(std::size_t nodes) const {
120 auto access = context_.require_ast_nodes(nodes, operation_);
121 if (!access) { throw access.error(); }
122 if (nodes > max_nodes_) {
123 throw CasError{CasErrc::ResourceLimit, kGrowthMessage, operation_};
124 }
125}
126
127void RewriteBudget::require_shape(const Shape& shape, std::size_t path_depth,
128 std::size_t active_depth) const {
129 require_nodes(shape.nodes);
130 const auto context_depth = context_.recursion_depth();
131 const auto context_limit = context_.limits().max_recursion_depth;
132 if (active_depth > max_depth_ || shape.height > max_depth_ - active_depth ||
133 path_depth > max_depth_ || shape.height > max_depth_ - path_depth ||
134 context_depth > context_limit || shape.height > context_limit - context_depth) {
135 throw CasError{CasErrc::ResourceLimit, kDepthMessage, operation_};
136 }
137}
138
140 const SymbolicNodePtr& node, std::size_t path_depth, std::size_t active_depth) {
142 auto step = context_.consume_steps(1, operation_);
143 if (!step) { throw step.error(); }
144 if (!node) { return {}; }
145 {
146 const auto found = shapes_.find(node.get());
147 if (found != shapes_.end()) {
148 const auto owner = found->second.owner.lock();
149 if (owner && owner.get() == node.get()) {
150 const auto shape = found->second.shape;
151 require_shape(shape, path_depth, active_depth);
152 return shape;
153 }
154 shapes_.erase(found);
155 }
156 }
157 if (path_depth >= max_depth_) {
158 throw CasError{CasErrc::ResourceLimit, kDepthMessage, operation_};
159 }
160 Shape shape;
161 {
162 MeasurementRecursion recursion(context_, operation_);
163 Measurement measurement(*this, path_depth, active_depth);
164 node->accept(measurement);
165 shape = measurement.shape;
166 }
167 require_shape(shape, path_depth, active_depth);
168 if (max_nodes_ != 0) {
169 if (shapes_.size() >= max_nodes_) {
170 auto cleared = context_.consume_steps(shapes_.size(), operation_);
171 if (!cleared) { throw cleared.error(); }
172 shapes_.clear();
173 }
174 shapes_.emplace(node.get(), Entry{node, shape});
175 }
176 return shape;
177}
178
180 return measure_shape(node, 0, depth_).nodes;
181}
182
184 // The result root replaces the current rewrite frame.
185 return measure_shape(node, 0, depth_ == 0 ? 0 : depth_ - 1).nodes;
186}
187
188std::size_t RewriteBudget::append_size(std::size_t current, std::size_t child) {
189 auto access = context_.consume_steps(0, operation_);
190 if (!access) { throw access.error(); }
191 if (child != 0 && current == 0) { consume(); }
192 if (current > max_nodes_ || child > max_nodes_ - current) {
193 throw CasError{CasErrc::ResourceLimit, kGrowthMessage, operation_};
194 }
195 return current + child;
196}
197
199 const std::shared_ptr<const SymbolicNode>& lhs,
200 const std::shared_ptr<const SymbolicNode>& rhs,
201 ComputationContext& context, std::size_t max_depth, const char* operation) {
202 RewriteBudget budget(context, max_depth, context.limits().max_ast_nodes, operation);
203 const auto left = budget.measure(lhs);
204 const auto right = budget.measure(rhs);
205 if (right > std::numeric_limits<std::size_t>::max() - left) {
206 throw CasError{CasErrc::ResourceLimit, kGrowthMessage, operation};
207 }
208 return left + right;
209}
210
211}
Result< void > consume_steps(std::size_t amount=1, const std::string &operation={})
Result< void > enter_recursion(const std::string &operation={})
Result< void > require_ast_nodes(std::size_t amount, const std::string &operation={}) const
std::size_t recursion_depth() const noexcept
Result< void > require_integer_bits(std::size_t amount, const std::string &operation={}) const
const ResourceLimits & limits() const noexcept
const std::variant< BigInt, Rational > & value() const noexcept
Measurement(RewriteBudget &budget, std::size_t path_depth, std::size_t active_depth)
void visit(const NumberNode &number) override
void visit_child(const SymbolicNodePtr &child) override
std::unordered_map< const SymbolicNode *, Entry > shapes_
ComputationContext & context() const noexcept
std::size_t measure(const std::shared_ptr< const SymbolicNode > &node)
void require_nodes(std::size_t nodes) const
std::size_t append_size(std::size_t current, std::size_t child)
RewriteBudget(ComputationContext &context, std::size_t max_depth, std::size_t max_nodes, const char *operation)
std::size_t measure_result(const std::shared_ptr< const SymbolicNode > &node)
std::size_t max_nodes() const noexcept
Shape measure_shape(const std::shared_ptr< const SymbolicNode > &node, std::size_t path_depth, std::size_t active_depth)
static std::size_t input_nodes(const std::shared_ptr< const SymbolicNode > &lhs, const std::shared_ptr< const SymbolicNode > &rhs, ComputationContext &context, std::size_t max_depth, const char *operation)
void require_shape(const Shape &shape, std::size_t path_depth, std::size_t active_depth) const
MeasurementRecursion(ComputationContext &context, const std::string &operation)
MeasurementRecursion & operator=(const MeasurementRecursion &)=delete
多元 Hensel 提升(单变量步)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
ExactRealAlgebraic rhs
std::shared_ptr< const SymbolicNode > SymbolicNodePtr
ComputationContext const std::string & operation
std::size_t std::size_t ComputationContext & context
ExprResult integer(Integer)=delete
std::size_t max_recursion_depth
Simultaneous checked depth.
const ExprPtr & right
ExprResult rational(const Rational &value)
内部 AST 定义。