LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
inference_engine_monotonicity.cpp
浏览该文件的文档.
1#define _USE_MATH_DEFINES
5
6namespace LMCAS {
7
11 interval.lower, context, "inference.logarithm_monotonicity");
12 if (!lower) return Monotonicity::Unknown;
13 auto comparison = detail::compare_comparable(
15 if (!comparison) return Monotonicity::Unknown;
16 if (comparison.value() > 0 ||
17 (comparison.value() == 0 && interval.lower.is_open)) {
19 }
21}
22
24 const FunctionNode& func,
25 const std::string& var,
26 const Interval& interval) {
27 if (func.arguments().empty()) {
29 }
30 auto arg_var = std::dynamic_pointer_cast<const VariableNode>(func.arguments()[0]);
31 if (!arg_var || arg_var->is_constant() || arg_var->name() != var) {
33 }
34
35 switch (func.type()) {
38
41
44 }
45
46 default:
47 break;
48 }
50}
51
53 const std::shared_ptr<const NumberNode>& number) {
54 BigInt exponent;
55 if (!try_get_integer_value(number, exponent) ||
56 exponent <= BigInt(0) || exponent > BigInt(100)) {
57 return std::nullopt;
58 }
59 return static_cast<int>(*exponent.try_to_int64());
60}
61
63 const std::shared_ptr<const SymbolicNode>& node,
64 const std::string& lhs_name,
65 const std::string& rhs_name,
66 std::unordered_set<int>& exponents) {
67 if (!node) {
68 return;
69 }
70 auto pow_node = std::dynamic_pointer_cast<const PowerNode>(node);
71 if (!pow_node) {
72 return;
73 }
74
75 auto base_var = std::dynamic_pointer_cast<const VariableNode>(pow_node->base());
76 if (!base_var || base_var->is_constant()) {
77 return;
78 }
79 if (base_var->name() != lhs_name && base_var->name() != rhs_name) {
80 return;
81 }
82
83 auto exp_num = std::dynamic_pointer_cast<const NumberNode>(pow_node->exponent());
84 if (!exp_num) {
85 return;
86 }
87 const auto exponent = small_positive_exponent(exp_num);
88 if (exponent) {
89 exponents.insert(*exponent);
90 }
91}
92
94 const RelationStore& store,
95 const std::string& lhs_name,
96 const std::string& rhs_name) {
97 std::unordered_set<int> exponents;
98 for (const auto& rel : store.get_relations()) {
99 collect_node_exponent(detail::node(rel.lhs), lhs_name, rhs_name, exponents);
100 collect_node_exponent(detail::node(rel.rhs), lhs_name, rhs_name, exponents);
101 }
102 return std::vector<int>(exponents.begin(), exponents.end());
103}
104
106 InferenceEngine& engine,
107 const SymbolicExpr& new_lhs,
108 const SymbolicExpr& new_rhs,
109 RelationStore& store,
110 PropertyStore& prop_store,
111 int depth) const {
112 if (store.has_relation(new_lhs, new_rhs, RelationalNode::Op::GT)) {
113 return Result<void>::success();
114 }
115
116 auto inserted = store.add_relation_checked(
117 new_lhs, new_rhs, RelationalNode::Op::GT, prop_store);
118 if (!inserted.has_value()) {
119 return Result<void>::failure(inserted.error());
120 }
121
122 Relation new_rel{new_lhs, new_rhs, RelationalNode::Op::GT};
123 return engine.apply_monotonicity_rules_checked(new_rel, store, prop_store, depth + 1);
124}
125
127 InferenceEngine& engine,
128 const VariableNode& lhs,
129 const VariableNode& rhs,
131 RelationStore& store,
132 PropertyStore& prop_store,
133 int depth) const {
134 auto lhs_node = detail::make_node<FunctionNode>(
135 type, std::vector<std::shared_ptr<const SymbolicNode>>{lhs.clone()});
136 auto rhs_node = detail::make_node<FunctionNode>(
137 type, std::vector<std::shared_ptr<const SymbolicNode>>{rhs.clone()});
138 auto lhs_expr = detail::expression_from_node(lhs_node);
139 auto rhs_expr = detail::expression_from_node(rhs_node);
140 return add_deduced(engine, lhs_expr, rhs_expr, store, prop_store, depth);
141}
142
144 InferenceEngine& engine,
145 const VariableNode& lhs,
146 const VariableNode& rhs,
147 RelationStore& store,
148 PropertyStore& prop_store,
149 int depth) const {
150 std::vector<int> exponents = collect_power_exponents(store, lhs.name(), rhs.name());
151
152 for (int n : exponents) {
153 auto exp_node = LMCAS::detail::make_node<NumberNode>(BigInt(n));
154 auto pow_x_node = LMCAS::detail::make_node<PowerNode>(lhs.clone(), exp_node->clone());
155 auto pow_y_node = LMCAS::detail::make_node<PowerNode>(rhs.clone(), exp_node->clone());
156
157 auto pow_x = LMCAS::detail::expression_from_node(pow_x_node);
158 auto pow_y = LMCAS::detail::expression_from_node(pow_y_node);
159 auto deduced = add_deduced(engine, pow_x, pow_y, store, prop_store, depth);
160 if (!deduced.has_value()) {
161 return deduced;
162 }
163 }
164 return Result<void>::success();
165}
166
167// Monotonicity inference
168
169/**
170 * @brief If the node is multiplication by exactly -1, returns the other
171 * operand.
172 */
173static std::shared_ptr<const SymbolicNode> detect_negation(
174 const std::shared_ptr<const SymbolicNode>& node) {
175 const auto multiply = std::dynamic_pointer_cast<const MultiplyNode>(node);
176 if (!multiply || multiply->operands().size() != 2) {
177 return nullptr;
178 }
179 for (std::size_t index = 0; index < 2; ++index) {
180 const auto number = std::dynamic_pointer_cast<const NumberNode>(
181 multiply->operands()[index]);
182 if (number && number->is_negative_one()) {
183 return multiply->operands()[1 - index];
184 }
185 }
186 return nullptr;
187}
188
189static bool is_variable_operand(const std::shared_ptr<const VariableNode>& node) {
190 return node && !node->is_constant();
191}
192
193Monotonicity InferenceEngine::infer_monotonicity(const SymbolicExpr& expr,
194 const std::string& var,
195 const Interval& interval) const {
196 if (!LMCAS::detail::node(expr)) {
198 }
199
200 // FunctionNode: auto-infer for known functions
201 if (auto func = std::dynamic_pointer_cast<const FunctionNode>(LMCAS::detail::node(expr))) {
202 return Impl::function_monotonicity(*func, var, interval);
203 }
204
205 // Negation: multiply by -1 reverses monotonicity
206 if (auto inner = detect_negation(LMCAS::detail::node(expr))) {
207 auto inner_expr = LMCAS::detail::expression_from_node(inner);
208 Monotonicity inner_mono = infer_monotonicity(inner_expr, var, interval);
209 switch (inner_mono) {
214 default: return Monotonicity::Unknown;
215 }
216 }
217
218 // VariableNode: check PropertyStore for declared monotonicity
219 if (auto var_node = std::dynamic_pointer_cast<const VariableNode>(LMCAS::detail::node(expr))) {
220 if (var_node->is_constant()) return Monotonicity::Unknown;
221 const auto& props = impl_->ctx;
222 auto monotonicity =
223 props.get_monotonicity_checked(var_node->name(), var, interval);
224 return monotonicity ? monotonicity.value() : Monotonicity::Unknown;
225 }
226
228}
229
230void InferenceEngine::apply_monotonicity_rules(const Relation& rel, RelationStore& store,
231 PropertyStore& prop_store, int depth) {
232 (void)apply_monotonicity_rules_checked(rel, store, prop_store, depth);
233}
234
235Result<void> InferenceEngine::apply_monotonicity_rules_checked(
236 const Relation& rel,
237 RelationStore& store,
238 PropertyStore& prop_store,
239 int depth) {
240 // Stop recursion at maximum depth
241 if (depth >= MAX_MONOTONICITY_DEPTH) {
242 return Result<void>::success();
243 }
244
245 // Only apply to GT (greater-than) relations
246 if (rel.op != RelationalNode::Op::GT) {
247 return Result<void>::success();
248 }
249
250 // Extract LHS and RHS — both must be single VariableNodes
251 auto lhs_var = std::dynamic_pointer_cast<const VariableNode>(LMCAS::detail::node(rel.lhs));
252 auto rhs_var = std::dynamic_pointer_cast<const VariableNode>(LMCAS::detail::node(rel.rhs));
253 if (!is_variable_operand(lhs_var) || !is_variable_operand(rhs_var)) {
254 return Result<void>::success();
255 }
256
257 const std::string& x_name = lhs_var->name();
258 const std::string& y_name = rhs_var->name();
259
260
261 // Check domain assumptions using the AssumptionContext (read-through all scopes)
262 bool both_positive = impl_->ctx.has_sign(x_name, Sign::Positive) &&
263 impl_->ctx.has_sign(y_name, Sign::Positive);
264 bool both_real = impl_->ctx.has_domain(x_name, Domain::Real) &&
265 impl_->ctx.has_domain(y_name, Domain::Real);
266 bool both_nonnegative = impl_->ctx.has_sign(x_name, Sign::NonNegative) &&
267 impl_->ctx.has_sign(y_name, Sign::NonNegative);
268
269 if (both_positive) {
271 auto deduced = impl_->deduce_function_relation(
272 *this, *lhs_var, *rhs_var, type, store, prop_store, depth);
273 if (!deduced.has_value()) {
274 return deduced;
275 }
276 }
277 }
278
279 if (both_real) {
280 auto deduced = impl_->deduce_function_relation(
281 *this, *lhs_var, *rhs_var, FunctionNode::FuncType::Exp,
282 store, prop_store, depth);
283 if (!deduced.has_value()) {
284 return deduced;
285 }
286 }
287
288 if (both_nonnegative) {
289 return impl_->deduce_power_relations(
290 *this, *lhs_var, *rhs_var, store, prop_store, depth);
291 }
292
293 return Result<void>::success();
294}
295
296} // namespace LMCAS
函数节点,表示数学函数调用(三角函数、对数、特殊函数等)。
FuncType type() const noexcept
@ Sqrt
对数、绝对值、平方根。
const std::vector< std::shared_ptr< const SymbolicNode > > & arguments() const noexcept
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(T value)
变量节点,表示一个符号变量。
std::shared_ptr< const SymbolicNode > clone() const override
const std::string & name() const noexcept
Result< ComparableEndpoint > comparable_endpoint(const Endpoint &endpoint, ComputationContext &context, const std::string &operation)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
inline ::LMCAS::SymbolicExpr expression_from_node(SymbolicNodePtr root)
std::shared_ptr< const Node > make_node(Args &&... args)
Result< int > compare_comparable(const ComparableEndpoint &left, const ComparableEndpoint &right, ComputationContext &context)
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)
const ExprPtr & lower
@ Real
Real numbers
static bool is_variable_operand(const std::shared_ptr< const VariableNode > &node)
static std::shared_ptr< const SymbolicNode > detect_negation(const std::shared_ptr< const SymbolicNode > &node)
If the node is multiplication by exactly -1, returns the other operand.
Monotonicity
Monotonicity classification for functions over intervals.
@ NonDecreasing
Non-decreasing (weakly increasing)
@ Decreasing
Strictly decreasing
@ Unknown
Monotonicity not determined
@ NonIncreasing
Non-increasing (weakly decreasing)
@ Increasing
Strictly increasing
const ExprPtr & exponent
std::size_t std::size_t depth
const ExprPtr & rhs
A stored relational constraint between two symbolic expressions.
static void collect_node_exponent(const std::shared_ptr< const SymbolicNode > &node, const std::string &lhs_name, const std::string &rhs_name, std::unordered_set< int > &exponents)
Result< void > deduce_power_relations(InferenceEngine &engine, const VariableNode &lhs, const VariableNode &rhs, RelationStore &store, PropertyStore &prop_store, int depth) const
static Monotonicity logarithm_monotonicity(const Interval &interval)
Result< void > deduce_function_relation(InferenceEngine &engine, const VariableNode &lhs, const VariableNode &rhs, FunctionNode::FuncType type, RelationStore &store, PropertyStore &prop_store, int depth) const
static std::vector< int > collect_power_exponents(const RelationStore &store, const std::string &lhs_name, const std::string &rhs_name)
Result< void > add_deduced(InferenceEngine &engine, const SymbolicExpr &new_lhs, const SymbolicExpr &new_rhs, RelationStore &store, PropertyStore &prop_store, int depth) const
static Monotonicity function_monotonicity(const FunctionNode &func, const std::string &var, const Interval &interval)
static std::optional< int > small_positive_exponent(const std::shared_ptr< const NumberNode > &number)