LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
solver_assumptions.cpp
浏览该文件的文档.
1#include "solver.hpp"
7#include "poly_utils.hpp"
10#include <iostream>
11#include <vector>
12#include <string>
13#include <map>
14#include <algorithm>
15#include <numeric>
16#include <cmath>
17#include <set>
18#include <queue>
19#include <unordered_map>
20#include <optional>
21#include <limits>
22
23namespace LMCAS {
24namespace {
25
26
27/**
28 * @brief 从解表达式提取 double 数值。
29 * @return 纯数值表达式提取成功时写入 out_value 并返回 true。
30 */
31static bool try_get_numeric_value(const std::shared_ptr<SymbolicExpr>& expr, double& out_value) {
32 if (!expr || !LMCAS::detail::node(expr)) {
33 return false;
34 }
35 auto num = std::dynamic_pointer_cast<const NumberNode>(LMCAS::detail::node(expr));
36 if (!num) {
37 return false;
38 }
39
40 return solver_detail::solver_number_value(*num, out_value);
41}
42static bool is_integer_value(double v) {
43 if (!std::isfinite(v)) {
44 return false;
45 }
46 int eq;
47 lmmc_double_nearly_equal_tol(v, std::round(v), 1e-12, 1e-12, &eq);
48 return eq != 0;
49}
50
51}
52
58
61 if (!is_integer_value(value)) {
62 return true;
63 }
64 }
66 if (value < 0.0) {
67 return true;
68 }
69 }
71 int equal;
72 lmmc_double_nearly_equal(value, 0.0, &equal);
73 if (equal || value <= 0.0) {
74 return true;
75 }
76 }
77 return false;
78}
79
80static bool numeric_sign_excludes(double value, const AssumptionContext& ctx,
81 const std::string& variable) {
82 bool exclude = false;
83 if (ctx.has_sign(variable, Sign::NonNegative) && value < 0.0) {
84 exclude = true;
85 }
86 if (ctx.has_sign(variable, Sign::Positive)) {
87 int equal;
88 lmmc_double_nearly_equal(value, 0.0, &equal);
89 if (equal || value <= 0.0) {
90 exclude = true;
91 }
92 }
93 if (ctx.has_sign(variable, Sign::Negative) && value >= 0.0) {
94 exclude = true;
95 }
96 if (ctx.has_sign(variable, Sign::NonPositive) && value > 0.0) {
97 exclude = true;
98 }
99 return exclude;
100}
101
102static const NumberNode* exact_scalar_number(const SymbolicNode* node) {
103 return dynamic_cast<const NumberNode*>(node);
104}
105
107 if (const auto* power = dynamic_cast<const PowerNode*>(node)) {
108 const auto* exponent = exact_scalar_number(power->exponent().get());
109 if (exponent && exact_number_as_rational(*exponent) == Rational(1, 2)) {
110 return exact_scalar_number(power->base().get());
111 }
112 }
113 if (const auto* function = dynamic_cast<const FunctionNode*>(node)) {
114 if (function->type() == FunctionNode::FuncType::Sqrt &&
115 function->arguments().size() == 1) {
116 return exact_scalar_number(function->arguments()[0].get());
117 }
118 }
119 return nullptr;
120}
121
122static bool collect_scalar_radical(const SymbolicNode* node, Rational& coefficient,
123 const NumberNode*& radicand) {
124 const auto* product = dynamic_cast<const MultiplyNode*>(node);
125 if (!product) {
126 radicand = exact_scalar_radicand(node);
127 return radicand != nullptr;
128 }
129 for (const auto& factor : product->operands()) {
130 if (const auto* number = exact_scalar_number(factor.get())) {
131 coefficient = coefficient * exact_number_as_rational(*number);
132 } else {
133 if (radicand) {
134 return false;
135 }
136 radicand = exact_scalar_radicand(factor.get());
137 if (!radicand) {
138 return false;
139 }
140 }
141 }
142 return radicand != nullptr;
143}
144
145static bool exact_rational_domain_excludes(const Rational& value, Domain domain) {
146 if (domain == Domain::Rational) {
147 return false;
148 }
149 if (!value.is_integer()) {
150 return true;
151 }
152 if (domain == Domain::Natural) {
153 return value < Rational(0);
154 }
156 return value <= Rational(0);
157 }
158 return false;
159}
160
164 return false;
165 }
166 Rational coefficient(1);
167 const NumberNode* radicand = nullptr;
168 if (!collect_scalar_radical(node, coefficient, radicand)) {
169 return false;
170 }
171 if (coefficient == Rational(0)) {
172 return exact_rational_domain_excludes(coefficient, domain);
173 }
174 if (exact_number_as_rational(*radicand) < Rational(0)) {
175 return false;
176 }
177 const auto root = normalization_exact_square_root(*radicand);
178 if (!root) {
179 return true;
180 }
182 coefficient * exact_number_as_rational(*root), domain);
183}
184
185static Result<bool> solution_is_excluded(const std::shared_ptr<SymbolicExpr>& solution,
186 Domain domain, const AssumptionContext& ctx,
187 const std::string& variable,
190 if (const auto* root = dynamic_cast<const RootOfNode*>(detail::node(solution).get())) {
191 auto isolated = detail::isolate_exact_root(
192 root->exact_id(), context, "solve_with_assumptions");
193 if (!isolated) return Result<bool>::failure(isolated.error());
194 if (std::holds_alternative<detail::ComplexIsolation>(isolated.value())) {
195 return true;
196 }
197 } else if (solver_detail::contains_imaginary(solution)) {
198 return true;
199 }
200 }
201 if (exact_radical_domain_excludes(detail::node(solution).get(), domain)) {
202 return true;
203 }
204 double value = 0.0;
205 if (!try_get_numeric_value(solution, value)) {
206 return false;
207 }
209 return true;
210 }
212}
213
215 const std::shared_ptr<SymbolicExpr>& equation,
216 const std::string& variable,
217 const AssumptionContext* ctx,
219{
220 auto solved = solve_finite_checked(
221 std::const_pointer_cast<SymbolicExpr>(equation), variable, context);
222 if (!solved) {
223 return AssumptionSolveResult::failure(solved.error());
224 }
225 auto all_solutions = std::move(solved.value());
226
227 if (!ctx) {
228 return all_solutions;
229 }
230
231 Domain domain = ctx->get_domain(variable);
232
233 std::vector<std::shared_ptr<SymbolicExpr>> filtered;
234 filtered.reserve(all_solutions.size());
235
236 bool has_sign_constraint = ctx->has_sign(variable, Sign::NonNegative) ||
237 ctx->has_sign(variable, Sign::Positive) ||
238 ctx->has_sign(variable, Sign::Negative) ||
239 ctx->has_sign(variable, Sign::NonPositive);
240
241 if (domain == Domain::Complex && !has_sign_constraint) {
242 return all_solutions;
243 }
244
245 for (const auto& sol : all_solutions) {
246 if (!sol) {
247 continue;
248 }
249
250 auto excluded = solution_is_excluded(sol, domain, *ctx, variable, context);
251 if (!excluded) return AssumptionSolveResult::failure(excluded.error());
252 if (!excluded.value()) {
253 filtered.push_back(sol);
254 }
255 }
256
257 return filtered;
258}
259
261 const std::shared_ptr<SymbolicExpr>& equation,
262 const std::string& variable,
263 const AssumptionContext* assumptions,
265{
266 constexpr const char* operation = "solve_with_assumptions";
267 if (!equation || variable.empty()) {
270 "assumption-aware solve requires an equation and variable",
271 operation);
272 }
273 auto budget = context.consume_steps(1, operation);
274 if (!budget) {
275 return AssumptionSolveResult::failure(budget.error());
276 }
277 try {
279 equation, variable, assumptions, context);
280 } catch (const CasError& error) {
281 return AssumptionSolveResult::failure(error);
282 } catch (const std::bad_alloc&) {
285 "allocation failed while solving with assumptions",
286 operation);
287 } catch (const std::length_error& error) {
288 return AssumptionSolveResult::failure(CasErrc::ResourceLimit, error.what(), operation);
289 } catch (const std::exception& ex) {
291 }
292}
293
295 const std::shared_ptr<SymbolicExpr>& equation,
296 const std::string& variable,
297 const AssumptionContext* assumptions)
298{
301 equation, variable, assumptions, context);
302}
303
304
305} // namespace LMCAS
AssumptionContext class with scoped push/pop management.
函数节点,表示数学函数调用(三角函数、对数、特殊函数等)。
@ Sqrt
对数、绝对值、平方根。
乘法节点,表示多个操作数的乘积。
幂运算节点,表示 base^exponent。
static Result failure(CasErrc code, std::string message, std::string operation={})
static bool try_get_numeric_value(const std::shared_ptr< SymbolicExpr > &expr, double &out_value)
从解表达式提取 double 数值。
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
Result< RootIsolation > isolate_exact_root(const ExactRootId &root, ComputationContext &context, const std::string &operation)
std::shared_ptr< SymbolicExpr > num(int value)
bool solver_number_value(const NumberNode &number, double &value)
bool contains_imaginary(const std::shared_ptr< SymbolicExpr > &expression)
const std::string & variable
ExprResult eq(const ExprPtr &lhs, const ExprPtr &rhs, ComputationContext &context)
ExprResult e()
Result< std::vector< std::shared_ptr< SymbolicExpr > > > AssumptionSolveResult
__attribute__((visibility("default"))) VectorCalculusExprResult surface_integral_scalar_checked(const std __attribute__((visibility("default"))) VectorCalculusExprResult surface_integral_scalar_checked(const std __attribute__((visibility("default"))) std const VectorField const std::string const std::string & v
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
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, ...)
static bool collect_scalar_radical(const SymbolicNode *node, Rational &coefficient, const NumberNode *&radicand)
static bool exact_radical_domain_excludes(const SymbolicNode *node, Domain domain)
AssumptionSolveResult solve_with_assumptions_checked(const std::shared_ptr< SymbolicExpr > &equation, const std::string &variable, const AssumptionContext *assumptions, ComputationContext &context)
static AssumptionSolveResult solve_with_assumptions_impl(const std::shared_ptr< SymbolicExpr > &equation, const std::string &variable, const AssumptionContext *ctx, ComputationContext &context)
std::shared_ptr< const NumberNode > normalization_exact_square_root(const NumberNode &number)
static bool numeric_domain_excludes(double value, Domain domain)
const ExprPtr & exponent
LMCAS::FiniteSolveResult solve_finite_checked(const std::shared_ptr< SymbolicExpr > &expr, const std::string &var, ComputationContext &context, const SolveOptions &opts)
static Result< bool > solution_is_excluded(const std::shared_ptr< SymbolicExpr > &solution, Domain domain, const AssumptionContext &ctx, const std::string &variable, ComputationContext &context)
const NumberDomainSet & domain
static bool numeric_sign_excludes(double value, const AssumptionContext &ctx, const std::string &variable)
static const NumberNode * exact_scalar_radicand(const SymbolicNode *node)
const std::string const ExprPtr & value
Rational exact_number_as_rational(const NumberNode &node)
static const NumberNode * exact_scalar_number(const SymbolicNode *node)
ExprResult function(const std::string &name, std::vector< ExprPtr > arguments)
static bool exact_rational_domain_excludes(const Rational &value, Domain domain)
static bool domain_requires_real(Domain domain)
多项式转换与精确符号多项式代数。
方程求解策略调度器 solve_dispatch 及相关选项结构体.
多项式方程组求解器 Solver,包含 Gröbner 基、线性系统求解。
内部 AST 定义。