LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
interval_endpoint.cpp
浏览该文件的文档.
1#include <stdexcept>
7#include <cmath>
8
9namespace LMCAS::detail {
10
13 const std::string& operation) {
14 if (!std::isfinite(value)) {
17 "finite interval endpoint evaluated to NaN or infinity",
18 operation);
19 }
20 if (value == 0.0) {
21 return Result<Rational>::success(Rational(0));
22 }
23
24 int exponent = 0;
25 std::frexp(value, &exponent);
26 const int binary_exponent = exponent - 53;
27 const std::size_t required_bits = binary_exponent < 0
28 ? static_cast<std::size_t>(-binary_exponent) + 1
29 : static_cast<std::size_t>(binary_exponent) + 54;
30 auto bit_budget = context.require_integer_bits(required_bits, operation);
31 if (!bit_budget) {
32 return Result<Rational>::failure(bit_budget.error());
33 }
34
35 return Result<Rational>::success(Rational::from_double(value));
36}
37
38namespace {
39
40
41std::optional<ComparableEndpoint> parse_quadratic_surd(
42 const std::shared_ptr<const SymbolicNode>& node);
43
44std::optional<ComparableEndpoint> combine_surd_sum(
45 const ComparableEndpoint& left, const ComparableEndpoint& right) {
46 if (left.infinity != 0 || right.infinity != 0) {
47 return std::nullopt;
48 }
49 if (left.radical_coefficient != Rational(0) &&
50 right.radical_coefficient != Rational(0) &&
51 left.radicand != right.radicand) {
52 return std::nullopt;
53 }
54 const Rational radicand = left.radical_coefficient != Rational(0)
55 ? left.radicand : right.radicand;
56 return ComparableEndpoint{
57 0,
58 left.rational + right.rational,
59 left.radical_coefficient + right.radical_coefficient,
60 radicand};
61}
62
63std::optional<ComparableEndpoint> combine_surd_product(
64 const ComparableEndpoint& left, const ComparableEndpoint& right) {
65 if (left.infinity != 0 || right.infinity != 0) {
66 return std::nullopt;
67 }
68 const bool left_radical = left.radical_coefficient != Rational(0);
69 const bool right_radical = right.radical_coefficient != Rational(0);
70 if (left_radical && right_radical && left.radicand != right.radicand) {
71 return std::nullopt;
72 }
73 const Rational radicand = left_radical ? left.radicand : right.radicand;
74 const Rational rational = left.rational * right.rational +
75 left.radical_coefficient * right.radical_coefficient * radicand;
76 const Rational coefficient =
77 left.rational * right.radical_coefficient +
78 left.radical_coefficient * right.rational;
79 return ComparableEndpoint{0, rational, coefficient, radicand};
80}
81
82std::optional<ComparableEndpoint> parse_surd_sum(const AddNode& add) {
83 ComparableEndpoint result{0, Rational(0), Rational(0), Rational(0)};
84 for (const auto& operand : add.operands()) {
85 auto parsed = parse_quadratic_surd(operand);
86 if (!parsed) {
87 return std::nullopt;
88 }
89 auto combined = combine_surd_sum(result, *parsed);
90 if (!combined) {
91 return std::nullopt;
92 }
93 result = std::move(*combined);
94 }
95 return result;
96}
97
98std::optional<ComparableEndpoint> parse_surd_product(const MultiplyNode& multiply) {
99 ComparableEndpoint result{0, Rational(1), Rational(0), Rational(0)};
100 for (const auto& operand : multiply.operands()) {
101 auto parsed = parse_quadratic_surd(operand);
102 if (!parsed) {
103 return std::nullopt;
104 }
105 auto combined = combine_surd_product(result, *parsed);
106 if (!combined) {
107 return std::nullopt;
108 }
109 result = std::move(*combined);
110 }
111 return result;
112}
113
114std::optional<ComparableEndpoint> parse_surd_power(const PowerNode& power) {
115 auto exponent = exact_rational_value(power.exponent());
116 if (!exponent) {
117 return std::nullopt;
118 }
119 if (*exponent == Rational(1, 2)) {
120 auto radicand = exact_rational_value(power.base());
121 if (!radicand || *radicand <= Rational(0)) {
122 return std::nullopt;
123 }
124 return ComparableEndpoint{0, Rational(0), Rational(1), *radicand};
125 }
126 auto base = parse_quadratic_surd(power.base());
127 if (!base || *exponent != Rational(-1)) {
128 return std::nullopt;
129 }
130 const Rational norm = base->rational * base->rational -
131 base->radical_coefficient * base->radical_coefficient * base->radicand;
132 if (norm == Rational(0)) {
133 return std::nullopt;
134 }
135 return ComparableEndpoint{
136 0,
137 base->rational / norm,
138 (Rational(0) - base->radical_coefficient) / norm,
139 base->radicand};
140}
141
142std::optional<ComparableEndpoint> parse_quadratic_surd(
143 const std::shared_ptr<const SymbolicNode>& node) {
144 if (!node) {
145 return std::nullopt;
146 }
147 if (auto number = exact_rational_value(node)) {
148 return ComparableEndpoint{0, *number, Rational(0), Rational(0)};
149 }
150
151 if (auto function = std::dynamic_pointer_cast<const FunctionNode>(node)) {
152 if (function->type() != FunctionNode::FuncType::Sqrt ||
153 function->arguments().size() != 1) {
154 return std::nullopt;
155 }
156 auto radicand = exact_rational_value(function->arguments()[0]);
157 if (!radicand || *radicand <= Rational(0)) {
158 return std::nullopt;
159 }
160 return ComparableEndpoint{0, Rational(0), Rational(1), *radicand};
161 }
162
163 if (auto add = std::dynamic_pointer_cast<const AddNode>(node)) {
164 return parse_surd_sum(*add);
165 }
166 if (auto multiply = std::dynamic_pointer_cast<const MultiplyNode>(node)) {
167 return parse_surd_product(*multiply);
168 }
169 if (auto power = std::dynamic_pointer_cast<const PowerNode>(node)) {
170 return parse_surd_power(*power);
171 }
172 return std::nullopt;
173}
174
176 const NumberNode& number, ComputationContext&,
177 const std::string&) {
178 if (std::holds_alternative<BigInt>(number.value())) {
180 0, Rational(std::get<BigInt>(number.value())),
181 Rational(0), Rational(0)});
182 }
184 0, std::get<Rational>(number.value()), Rational(0), Rational(0)});
185}
186
189 if (surd.radical_coefficient != Rational(0)) {
190 const Rational a = surd.rational;
191 const Rational b = surd.radical_coefficient;
192 const Rational d = surd.radicand;
195 a * a - b * b * d,
196 Rational(-2) * a,
197 Rational(1)
198 }),
199 b > Rational(0) ? 1 : 0, 1, context);
200 if (!algebraic) {
201 return Result<ComparableEndpoint>::failure(algebraic.error());
202 }
203 surd.algebraic = std::move(algebraic.value());
204 }
205 return Result<ComparableEndpoint>::success(std::move(surd));
206}
207
210 const std::string& operation) {
211 auto isolated = LMCAS::detail::isolate_exact_root(
212 root.exact_id(), context, operation);
213 if (!isolated) {
214 return Result<ComparableEndpoint>::failure(isolated.error());
215 }
216 auto* real = std::get_if<LMCAS::detail::RealIsolation>(
217 &isolated.value());
218 if (!real) {
221 "interval endpoint RootOf is not real",
222 operation);
223 }
224 ComparableEndpoint endpoint_value;
225 endpoint_value.algebraic = std::move(real->value);
226 return Result<ComparableEndpoint>::success(std::move(endpoint_value));
227}
228
230 if (function.arguments().size() != 1) {
231 return false;
232 }
233 auto argument = exact_rational_value(function.arguments()[0]);
234 if (!argument) {
235 return false;
236 }
237 if (function.type() == FunctionNode::FuncType::Ln) {
238 return *argument <= Rational(0);
239 }
240 return function.type() == FunctionNode::FuncType::Sqrt &&
241 *argument < Rational(0);
242}
243
245 const std::shared_ptr<const SymbolicNode>& node, const std::string& operation) {
246 if (auto variable = std::dynamic_pointer_cast<const VariableNode>(node);
247 variable && !variable->is_constant()) {
250 "interval endpoint contains unbound symbol '" + variable->name() + "'",
251 operation);
252 }
253 if (auto function = std::dynamic_pointer_cast<const FunctionNode>(node)) {
257 "interval endpoint is outside the real function domain",
258 operation);
259 }
260 }
263 "exact ordering of a symbolic interval endpoint is not proven",
264 operation);
265}
266
268 const Endpoint& endpoint, ComputationContext& context,
269 const std::string& operation) try {
270 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
271 (context).limits().max_ast_nodes, "transform");
272 if (!endpoint.value || !LMCAS::detail::node(endpoint.value)) {
275 "finite interval endpoint must contain an expression",
276 operation);
277 }
278
279 auto simplified = detail::make_expression_ptr(detail::normalize_node(detail::node(endpoint.value), transform_budget));
280 if (!simplified || !LMCAS::detail::node(simplified)) {
283 "interval endpoint simplification produced a null expression",
284 operation);
285 }
286
287 const auto& node = LMCAS::detail::node(simplified);
288 if (auto number = std::dynamic_pointer_cast<const NumberNode>(node)) {
289 return comparable_number(*number, context, operation);
290 }
291 if (auto surd = parse_quadratic_surd(node)) {
292 return comparable_surd(std::move(*surd), context);
293 }
294 if (auto root = std::dynamic_pointer_cast<const RootOfNode>(node)) {
295 return comparable_root(*root, context, operation);
296 }
298} catch (const CasError& error) {
300} catch (const std::bad_alloc&) {
302} catch (const std::length_error& error) {
304} catch (const std::exception& error) {
306}
307
308}
309
311 const Endpoint& endpoint,
313 const std::string& operation) {
314 auto step = context.consume_steps(1, operation);
315 if (!step) {
316 return Result<ComparableEndpoint>::failure(step.error());
317 }
318
319 if (endpoint.is_neg_infinity && endpoint.is_pos_infinity) {
322 "an endpoint cannot be both negative and positive infinity",
323 operation);
324 }
325 if (endpoint.is_neg_infinity || endpoint.is_pos_infinity) {
326 if (endpoint.value) {
329 "infinite endpoints cannot also contain a finite value",
330 operation);
331 }
332 if (!endpoint.is_open) {
335 "infinite interval endpoints must be open",
336 operation);
337 }
339 ComparableEndpoint{endpoint.is_neg_infinity ? -1 : 1,
340 Rational(0), Rational(0), Rational(0)});
341 }
342 return comparable_finite_endpoint(endpoint, context, operation);
343}
344
345}
加法节点,表示多个操作数的求和。
函数节点,表示数学函数调用(三角函数、对数、特殊函数等)。
@ Sqrt
对数、绝对值、平方根。
乘法节点,表示多个操作数的乘积。
const std::variant< BigInt, Rational > & value() const noexcept
一元多项式模板类
幂运算节点,表示 base^exponent。
const std::shared_ptr< const SymbolicNode > & exponent() const noexcept
const std::shared_ptr< const SymbolicNode > & base() const noexcept
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(T value)
const LMCAS::detail::ExactRootId & exact_id() const noexcept
std::optional< ComparableEndpoint > combine_surd_sum(const ComparableEndpoint &left, const ComparableEndpoint &right)
std::optional< ComparableEndpoint > parse_surd_product(const MultiplyNode &multiply)
std::optional< ComparableEndpoint > combine_surd_product(const ComparableEndpoint &left, const ComparableEndpoint &right)
Result< ComparableEndpoint > unsupported_endpoint(const std::shared_ptr< const SymbolicNode > &node, const std::string &operation)
Result< ComparableEndpoint > comparable_number(const NumberNode &number, ComputationContext &, const std::string &)
Result< ComparableEndpoint > comparable_surd(ComparableEndpoint surd, ComputationContext &context)
Result< ComparableEndpoint > comparable_root(const RootOfNode &root, ComputationContext &context, const std::string &operation)
std::optional< ComparableEndpoint > parse_surd_power(const PowerNode &power)
std::optional< ComparableEndpoint > parse_quadratic_surd(const std::shared_ptr< const SymbolicNode > &node)
Result< ComparableEndpoint > comparable_finite_endpoint(const Endpoint &endpoint, ComputationContext &context, const std::string &operation)
std::optional< ComparableEndpoint > parse_surd_sum(const AddNode &add)
多元 Hensel 提升(单变量步)
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< Rational > exact_double_rational(double value, ComputationContext &context, const std::string &operation)
Result< RootIsolation > isolate_exact_root(const ExactRootId &root, ComputationContext &context, const std::string &operation)
const std::string & variable
ComputationContext const std::string & operation
std::optional< Rational > exact_rational_value(const NumberNode &number)
SymbolicNodePtr normalize_node(const SymbolicNodePtr &node, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
ExactRealAlgebraicResult make_exact_real_algebraic(Polynomial< Rational > polynomial, std::size_t root_index, std::size_t multiplicity, ComputationContext &context)
std::size_t std::size_t ComputationContext & context
ExprResult real(const ExprPtr &expression, ComputationContext &context)
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
const std::string const ExprPtr & value
ExprResult function(const std::string &name, std::vector< ExprPtr > arguments)
ExprResult rational(const Rational &value)
std::optional< LMCAS::detail::ExactRealAlgebraic > algebraic
内部 AST 定义。