LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
equivalence_engine.cpp
浏览该文件的文档.
2
3#include "quantity.hpp"
6
7#include <exception>
8#include <limits>
9#include <memory>
10#include <new>
11#include <utility>
12
13namespace LMCAS {
14namespace equivalence_detail {
15
17 if (!expression || !detail::node(expression)) {
19 "equivalence normalization returned null", kEquivalentOperation};
20 }
21 auto result = detail::make_expression_ptr(
22 detail::normalize_node(detail::node(expression), budget));
23 return result;
24}
25
27 if (!expression || !detail::node(expression)) {
29 "equivalence expansion returned null", kEquivalentOperation};
30 }
31 auto result = detail::make_expression_ptr(
32 detail::expand_node(detail::node(expression), budget));
33 return result;
34}
35
36}
37namespace {
38
39Result<bool> equivalent_with_budget(const SymbolicExpr& lhs, const SymbolicExpr& rhs,
41 detail::RewriteBudget* budget);
42Result<bool> prove_operands(const SymbolicExpr& lhs, const SymbolicExpr& rhs,
45
47 const SymbolicExpr& lhs, const SymbolicExpr& rhs,
49 using OperandsResult = Result<std::pair<ExprPtr, ExprPtr>>;
50 auto left = std::make_shared<SymbolicExpr>(lhs);
51 auto right = std::make_shared<SymbolicExpr>(rhs);
52 if (auto quantity = std::dynamic_pointer_cast<const QuantityNode>(detail::node(lhs))) {
53 normalization_check_arithmetic<MultiplyNode>(&budget, 1, quantity->value());
54 auto stripped = strip_unit(left, UnitStripMode::BaseValue, context);
55 if (!stripped) { return OperandsResult::failure(stripped.error()); }
56 left = stripped.value();
57 }
58 if (auto quantity = std::dynamic_pointer_cast<const QuantityNode>(detail::node(rhs))) {
59 normalization_check_arithmetic<MultiplyNode>(&budget, 1, quantity->value());
61 if (!stripped) { return OperandsResult::failure(stripped.error()); }
62 right = stripped.value();
63 }
64 return OperandsResult::success({std::move(left), std::move(right)});
65}
66Result<bool> apply_profile(const SymbolicExpr& lhs, const SymbolicExpr& rhs,
69 if (options.profile != EqvProfile::TrigBasic &&
71 return prove_operands(lhs, rhs, context, options, budget);
72 }
73 const bool trig = options.profile == EqvProfile::TrigBasic;
74 budget.consume();
75 ExprPtr rewritten_lhs;
76 ExprPtr rewritten_rhs;
77 if (trig) {
80 } else {
82 detail::node(lhs), context.assumptions().get(), budget);
84 detail::node(rhs), context.assumptions().get(), budget);
85 }
86 rewritten_lhs = equivalence_detail::normalize_equivalence(rewritten_lhs, budget);
87 rewritten_rhs = equivalence_detail::normalize_equivalence(rewritten_rhs, budget);
88 EqvOptions core_options = options;
89 core_options.profile = EqvProfile::Core;
90 return equivalent_with_budget(*rewritten_lhs, *rewritten_rhs, context, core_options, &budget);
91}
92
93Result<bool> prove_operands(const SymbolicExpr& lhs, const SymbolicExpr& rhs,
96 budget.consume();
97 auto operands = prepare_operands(lhs, rhs, context, budget);
98 if (!operands) { return Result<bool>::failure(operands.error()); }
100 *operands.value().first, budget);
102 *operands.value().second, budget);
103 if (structurally_equal(*left, *right)) {
104 return Result<bool>::success(true);
105 }
107 auto negative = SymbolicExpr::multiply(SymbolicExpr::number(-1), right);
109 auto difference = SymbolicExpr::add(left, negative);
110 if (!difference) {
112 "equivalence difference construction failed",
114 }
115 difference = equivalence_detail::normalize_equivalence(difference, budget);
116 const auto value = detail::exact_rational_value(detail::node(difference));
117 if (value && value->is_zero()) {
118 return Result<bool>::success(true);
119 }
121 difference, context, options, budget);
122 if (!polynomial) { return Result<bool>::failure(polynomial.error()); }
123 if (polynomial.value()) { return Result<bool>::success(*polynomial.value()); }
125 "equivalence proof is inconclusive",
127}
128
130 const SymbolicExpr& lhs, const SymbolicExpr& rhs,
132 detail::RewriteBudget& budget) {
133 budget.consume();
134 budget.measure(detail::node(lhs));
135 budget.measure(detail::node(rhs));
136 if (structurally_equal(lhs, rhs)) { return true; }
137 auto lhs_dimension = dimension_of(lhs);
138 if (!lhs_dimension) { return Result<bool>::failure(lhs_dimension.error()); }
139 auto rhs_dimension = dimension_of(rhs);
140 if (!rhs_dimension) { return Result<bool>::failure(rhs_dimension.error()); }
141 if (lhs_dimension.value() != rhs_dimension.value()) {
142 return Result<bool>::success(false);
143 }
144 return options.profile == EqvProfile::Core
145 ? prove_operands(lhs, rhs, context, options, budget)
146 : apply_profile(lhs, rhs, context, options, budget);
147}
148
149Result<bool> equivalent_with_budget(const SymbolicExpr& lhs, const SymbolicExpr& rhs,
151 detail::RewriteBudget* budget) {
152 try {
154 if (!valid) { return Result<bool>::failure(valid.error()); }
155 auto step = context.consume_steps(1, kEquivalentOperation);
156 if (!step) { return Result<bool>::failure(step.error()); }
157 if (budget) {
158 return prove_checked_operands(lhs, rhs, context, options, *budget);
159 }
160 const auto input_nodes = detail::RewriteBudget::input_nodes(
162 options.budget.max_rewrite_depth, kEquivalentOperation);
163 const auto maximum = std::numeric_limits<std::size_t>::max();
164 const auto factor = options.budget.max_node_growth_factor;
165 const auto growth_nodes = input_nodes > maximum / factor
166 ? maximum : input_nodes * factor;
167 const auto max_nodes = std::min(context.limits().max_ast_nodes, growth_nodes);
168 detail::RewriteBudget initial_budget(
169 context, options.budget.max_rewrite_steps,
170 options.budget.max_rewrite_depth, max_nodes, kEquivalentOperation);
171 return prove_checked_operands(lhs, rhs, context, options, initial_budget);
172 } catch (const CasError& error) {
173 return Result<bool>::failure(error);
174 } catch (const std::length_error& error) {
177 } catch (const std::bad_alloc&) {
179 "equivalence check allocation failed",
181 } catch (const std::exception& error) {
184 }
185}
186
187}
188
189bool structurally_equal(const SymbolicExpr& lhs, const SymbolicExpr& rhs) {
190 const auto& left = detail::node(lhs);
191 const auto& right = detail::node(rhs);
192 if (!left || !right) return left == right;
193 return left->equals(*right);
194}
195
196Result<bool> detail::equivalent_impl(const SymbolicExpr& lhs,
197 const SymbolicExpr& rhs,
199 const EqvOptions& options) {
200 return equivalent_with_budget(lhs, rhs, context, options, nullptr);
201}
202
203
204}
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(T value)
std::size_t measure(const std::shared_ptr< const SymbolicNode > &node)
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)
Result< bool > apply_profile(const SymbolicExpr &lhs, const SymbolicExpr &rhs, ComputationContext &context, const EqvOptions &options, detail::RewriteBudget &budget)
Result< bool > prove_operands(const SymbolicExpr &lhs, const SymbolicExpr &rhs, ComputationContext &context, const EqvOptions &options, detail::RewriteBudget &budget)
Result< std::pair< ExprPtr, ExprPtr > > prepare_operands(const SymbolicExpr &lhs, const SymbolicExpr &rhs, ComputationContext &context, detail::RewriteBudget &budget)
Result< bool > prove_checked_operands(const SymbolicExpr &lhs, const SymbolicExpr &rhs, ComputationContext &context, const EqvOptions &options, detail::RewriteBudget &budget)
Result< bool > equivalent_with_budget(const SymbolicExpr &lhs, const SymbolicExpr &rhs, ComputationContext &context, const EqvOptions &options, detail::RewriteBudget *budget)
SymbolicNodePtr expand_node(const SymbolicNodePtr &node, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
std::shared_ptr<::LMCAS::SymbolicExpr > make_expression_ptr(SymbolicNodePtr root)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
ExactRealAlgebraic rhs
std::optional< Rational > exact_rational_value(const NumberNode &number)
SymbolicNodePtr normalize_node(const SymbolicNodePtr &node, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
std::size_t std::size_t ComputationContext & context
Result< bool > equivalent_impl(const SymbolicExpr &lhs, const SymbolicExpr &rhs, ComputationContext &context, const EqvOptions &options)
ExprPtr normalize_equivalence(const ExprPtr &expression, detail::RewriteBudget &budget)
Result< std::optional< bool > > prove_rational_polynomial_equivalence(const ExprPtr &difference, ComputationContext &context, const EqvOptions &options, detail::RewriteBudget &budget)
ExprPtr rewrite_trig_basic_identity(const detail::SymbolicNodePtr &node, detail::RewriteBudget &budget)
Result< void > validate_eqv_options(const EqvOptions &options)
ExprPtr expand_equivalence(const ExprPtr &expression, detail::RewriteBudget &budget)
ExprPtr rewrite_exp_log_basic_identity(const detail::SymbolicNodePtr &node, const AssumptionContext *assumptions, detail::RewriteBudget &budget)
ExprPtr canonicalize_complex_product(const SymbolicExpr &expression, detail::RewriteBudget &budget)
Result< DimensionSignature > dimension_of(const SymbolicExpr &expression)
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
const std::string ComputationContext const SolveOptions & options
std::size_t normalization_check_arithmetic(detail::RewriteBudget *budget, std::size_t extra_nodes, const std::shared_ptr< const Nodes > &... children)
std::shared_ptr< SymbolicExpr > ExprPtr
const ExprPtr & right
const std::string const ExprPtr & value
const ExprPtr & rhs
bool structurally_equal(const SymbolicExpr &lhs, const SymbolicExpr &rhs)
QuantityResult strip_unit(const std::shared_ptr< SymbolicExpr > &expression, UnitStripMode mode, ComputationContext &context)
constexpr const char * kEquivalentOperation