LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
solver_linear.cpp
浏览该文件的文档.
2#include "solver.hpp"
4#include "poly_utils.hpp"
8#include <iostream>
9#include <vector>
10#include <string>
11#include <map>
12#include <algorithm>
13#include <numeric>
14#include <cmath>
15#include <set>
16#include <queue>
17#include <unordered_map>
18#include <optional>
19#include <limits>
21
22namespace LMCAS {
23using namespace solver_detail;
24
25
26std::shared_ptr<SymbolicExpr> solver_detail::to_ptr(const SymbolicExpr& expr) {
28}
29
30static bool get_integer_value(const std::shared_ptr<const SymbolicNode>& node, BigInt& value) {
31 auto num = std::dynamic_pointer_cast<const NumberNode>(node);
32 if (!num) {
33 return false;
34 }
35 if (std::holds_alternative<BigInt>(num->value())) {
36 value = std::get<BigInt>(num->value());
37 return true;
38 }
39 if (std::holds_alternative<Rational>(num->value())) {
40 const auto& r = std::get<Rational>(num->value());
41 if (!r.is_integer()) {
42 return false;
43 }
44 value = r.to_bigint();
45 return true;
46 }
47 return false;
48}
49
50static std::shared_ptr<const NumberNode> add_number_nodes(const std::shared_ptr<const NumberNode>& a, const std::shared_ptr<const NumberNode>& b) {
51 if (std::holds_alternative<Rational>(a->value()) || std::holds_alternative<Rational>(b->value())) {
52 Rational r1 = std::holds_alternative<Rational>(a->value()) ? std::get<Rational>(a->value()) : Rational(std::get<BigInt>(a->value()));
53 Rational r2 = std::holds_alternative<Rational>(b->value()) ? std::get<Rational>(b->value()) : Rational(std::get<BigInt>(b->value()));
55 }
56
57 return LMCAS::detail::make_node<NumberNode>(std::get<BigInt>(a->value()) + std::get<BigInt>(b->value()));
58}
59
60static std::shared_ptr<const NumberNode> multiply_number_nodes(const std::shared_ptr<const NumberNode>& a, const std::shared_ptr<const NumberNode>& b) {
61 if (std::holds_alternative<Rational>(a->value()) || std::holds_alternative<Rational>(b->value())) {
62 Rational r1 = std::holds_alternative<Rational>(a->value()) ? std::get<Rational>(a->value()) : Rational(std::get<BigInt>(a->value()));
63 Rational r2 = std::holds_alternative<Rational>(b->value()) ? std::get<Rational>(b->value()) : Rational(std::get<BigInt>(b->value()));
65 }
66
67 return LMCAS::detail::make_node<NumberNode>(std::get<BigInt>(a->value()) * std::get<BigInt>(b->value()));
68}
69
70struct NodeLess {
71 bool operator()(const std::shared_ptr<const SymbolicNode>& a, const std::shared_ptr<const SymbolicNode>& b) const {
72 if (!a || !b) {
73 return a < b;
74 }
75 return a->compare(*b) < 0;
76 }
77};
78
79std::shared_ptr<SymbolicExpr> solver_detail::multiply_no_expand(
80 const std::shared_ptr<const SymbolicNode>& term,
81 const std::vector<std::shared_ptr<const SymbolicNode>>& den_factors
82) {
83 std::vector<std::shared_ptr<const SymbolicNode>> factors;
84 if (auto mul = std::dynamic_pointer_cast<const MultiplyNode>(term)) {
85 factors.insert(factors.end(), mul->operands().begin(), mul->operands().end());
86 } else if (term) {
87 factors.push_back(term);
88 }
89 factors.insert(factors.end(), den_factors.begin(), den_factors.end());
90
91 std::shared_ptr<const NumberNode> const_acc =
93 std::map<std::shared_ptr<const SymbolicNode>, std::shared_ptr<const NumberNode>, NodeLess> bases;
94
95 for (const auto& op : factors) {
96 if (!op) {
97 continue;
98 }
99 if (auto num = std::dynamic_pointer_cast<const NumberNode>(op)) {
100 const_acc = multiply_number_nodes(const_acc, num);
101 continue;
102 }
103
104 std::shared_ptr<const SymbolicNode> base = op;
105 std::shared_ptr<const NumberNode> exp = LMCAS::detail::make_node<NumberNode>(BigInt(1));
106 if (auto pow = std::dynamic_pointer_cast<const PowerNode>(op)) {
107 base = pow->base();
108 if (auto e_num = std::dynamic_pointer_cast<const NumberNode>(pow->exponent())) {
109 exp = e_num;
110 }
111 }
112
113 auto it = bases.find(base);
114 if (it == bases.end()) {
115 bases[base] = exp;
116 } else {
117 bases[base] = add_number_nodes(it->second, exp);
118 }
119 }
120
121 std::vector<std::shared_ptr<const SymbolicNode>> final_ops;
122 if (!const_acc->is_one()) final_ops.push_back(const_acc);
123
124 for (const auto& [base, exp] : bases) {
125 if (exp->is_zero()) {
126 continue;
127 }
128 if (exp->is_one()) final_ops.push_back(base);
129 else final_ops.push_back(LMCAS::detail::make_node<PowerNode>(base, exp));
130 }
131
132 if (final_ops.empty()) {
133 return SymbolicExpr::number(1);
134 }
135 if (final_ops.size() == 1) {
136 return LMCAS::detail::make_expression_ptr(final_ops[0]);
137 }
139}
140
141bool solver_detail::is_polynomial_node(const std::shared_ptr<const SymbolicNode>& node) {
142 if (!node) {
143 return false;
144 }
145 if (std::dynamic_pointer_cast<const NumberNode>(node)) {
146 return true;
147 }
148 if (std::dynamic_pointer_cast<const VariableNode>(node)) {
149 return true;
150 }
151
152 if (auto add = std::dynamic_pointer_cast<const AddNode>(node)) {
153 for (const auto& op : add->operands()) {
154 if (!is_polynomial_node(op)) {
155 return false;
156 }
157 }
158 return true;
159 }
160
161 if (auto mul = std::dynamic_pointer_cast<const MultiplyNode>(node)) {
162 for (const auto& op : mul->operands()) {
163 if (!is_polynomial_node(op)) {
164 return false;
165 }
166 }
167 return true;
168 }
169
170 if (auto pow = std::dynamic_pointer_cast<const PowerNode>(node)) {
171 BigInt exp(0);
172 if (!get_integer_value(pow->exponent(), exp)) {
173 return false;
174 }
175 if (exp < 0) {
176 return false;
177 }
178 return is_polynomial_node(pow->base());
179 }
180
181 return false;
182}
183
185 const std::vector<std::shared_ptr<const SymbolicNode>>& operands,
186 std::vector<std::shared_ptr<const SymbolicNode>>& den_factors,
187 std::vector<std::shared_ptr<SymbolicExpr>>& den_constraints) {
188 for (const auto& operand : operands) {
189 if (!collect_denominator_factors(operand, den_factors, den_constraints)) {
190 return false;
191 }
192 }
193 return true;
194}
195
197 const PowerNode& power,
198 std::vector<std::shared_ptr<const SymbolicNode>>& den_factors,
199 std::vector<std::shared_ptr<SymbolicExpr>>& den_constraints) {
200 BigInt exp(0);
201 if (!get_integer_value(power.exponent(), exp)) {
202 return false;
203 }
204
205 if (exp < 0) {
206 if (!is_polynomial_node(power.base())) {
207 return false;
208 }
209 BigInt k = -exp;
210 if (k == 1) {
211 den_factors.push_back(power.base());
212 } else {
213 den_factors.push_back(SymbolicFactory::create_power(power.base(), SymbolicFactory::create_number(k)));
214 }
215 den_constraints.push_back(LMCAS::detail::make_expression_ptr(power.base()));
216 return true;
217 }
218
219 if (!collect_denominator_factors(power.base(), den_factors, den_constraints)) {
220 return false;
221 }
222 return true;
223}
224
226 const std::shared_ptr<const SymbolicNode>& node,
227 std::vector<std::shared_ptr<const SymbolicNode>>& den_factors,
228 std::vector<std::shared_ptr<SymbolicExpr>>& den_constraints
229) {
230 if (!node) {
231 return false;
232 }
233
234 if (std::dynamic_pointer_cast<const NumberNode>(node) || std::dynamic_pointer_cast<const VariableNode>(node)) {
235 return true;
236 }
237
238 if (auto add = std::dynamic_pointer_cast<const AddNode>(node)) {
239 return collect_operand_denominators(add->operands(), den_factors, den_constraints);
240 }
241
242 if (auto mul = std::dynamic_pointer_cast<const MultiplyNode>(node)) {
243 return collect_operand_denominators(mul->operands(), den_factors, den_constraints);
244 }
245
246 if (auto pow = std::dynamic_pointer_cast<const PowerNode>(node)) {
247 return collect_power_denominators(*pow, den_factors, den_constraints);
248 }
249
250 if (auto func = std::dynamic_pointer_cast<const FunctionNode>(node)) {
251 return collect_operand_denominators(func->arguments(), den_factors, den_constraints);
252 }
253
254 return false;
255}
256
257static std::pair<SymbolicExpr, SymbolicExpr> isolate_linear_coeff(const SymbolicExpr& expr, const std::string& var, ComputationContext& context) {
258
259 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
260 (context).limits().max_ast_nodes, "transform");
261
262 auto expr_ptr = to_ptr(expr);
263 auto A_ptr = detail::make_expression_ptr(detail::differentiate_node(detail::node(expr_ptr), var, transform_budget));
264
265 std::vector<std::shared_ptr<const SymbolicNode>> mops;
266 mops.push_back(LMCAS::detail::node(A_ptr));
267 mops.push_back(SymbolicFactory::create_variable(var));
268 auto term_Ax = SymbolicFactory::create_multiply(mops);
269
270 std::vector<std::shared_ptr<const SymbolicNode>> nops;
271 nops.push_back(SymbolicFactory::create_number(BigInt(-1)));
272 nops.push_back(term_Ax);
273 auto neg_term = SymbolicFactory::create_multiply(nops);
274
275 std::vector<std::shared_ptr<const SymbolicNode>> aops;
276 aops.push_back(LMCAS::detail::node(expr));
277 aops.push_back(neg_term);
278 auto B_node = SymbolicFactory::create_add(aops);
279
280 auto B_expr = LMCAS::detail::expression_from_node(B_node);
281 auto B_simp = detail::make_expression_ptr(detail::normalize_node(detail::node(to_ptr(B_expr)), transform_budget));
282
284 auto A_simp = detail::make_expression_ptr(detail::normalize_node(detail::node(to_ptr(A_expr)), transform_budget));
285
288}
289
290std::map<std::string, SymbolicExpr> Solver::solve_linear_system(
291 const std::vector<SymbolicExpr>& equations_in,
292 const std::vector<std::string>& variables)
293{
294 ComputationContext context;
295 return solver_detail::project_unconditional_linear_system(equations_in, variables, context);
296}
297
298std::map<std::string, SymbolicExpr> solver_detail::project_unconditional_linear_system(
299 const std::vector<SymbolicExpr>& equations,
300 const std::vector<std::string>& variables, ComputationContext& context) {
301 try {
302 return solve_linear_system_impl(equations, variables, context);
303 } catch (const CasError& error) {
304 // 容器接口以空映射表示求解未决;受检算法保留内部错误分类。
305 if (error.code == CasErrc::Inconclusive ||
307 return {};
308 }
309 throw;
310 }
311}
312
313std::map<std::string, SymbolicExpr> solver_detail::solve_linear_system_impl(
314 const std::vector<SymbolicExpr>& equations_in,
315 const std::vector<std::string>& variables, ComputationContext& context) {
316 detail::RewriteBudget transform_budget(context, context.limits().max_recursion_depth,
317 context.limits().max_ast_nodes, "solve_linear_system");
318
319 size_t num_vars = variables.size();
320 size_t num_eqs = equations_in.size();
321
322 std::vector<std::vector<std::shared_ptr<SymbolicExpr>>> matrix(num_eqs, std::vector<std::shared_ptr<SymbolicExpr>>(num_vars + 1));
323
324 for (size_t i = 0; i < num_eqs; ++i) {
325 SymbolicExpr current_eq = equations_in[i];
326
327 SymbolicExpr constant_part = current_eq;
328
329 for (size_t j = 0; j < num_vars; ++j) {
330 auto [coeff, remainder] = isolate_linear_coeff(constant_part, variables[j], context);
331 matrix[i][j] = LMCAS::detail::make_expression_ptr(coeff);
332
333 constant_part = remainder;
334 }
335
336 std::vector<std::shared_ptr<const SymbolicNode>> ops;
337 ops.push_back(SymbolicFactory::create_number(BigInt(-1)));
338 ops.push_back(LMCAS::detail::node(constant_part));
339 auto neg_const = SymbolicFactory::create_multiply(ops);
340 matrix[i][num_vars] = LMCAS::detail::make_expression_ptr(neg_const);
341 }
342
343 detail::ExactMatrixData augmented{
344 num_eqs, num_vars + 1, {}};
345 augmented.entries.reserve(num_eqs * (num_vars + 1));
346 for (const auto& row : matrix) {
347 augmented.entries.insert(
348 augmented.entries.end(), row.begin(), row.end());
349 }
350
351 auto solved = detail::solve_linear_exact(
352 std::move(augmented), num_vars, context,
353 "solve_linear_system");
354 if (!solved) throw solved.error();
355 if (std::holds_alternative<detail::InconsistentLinearSolution>(solved.value())) {
356 return {};
357 }
358
359 std::vector<std::shared_ptr<SymbolicExpr>> values;
360 if (auto* unique =
361 std::get_if<detail::UniqueLinearSolution>(&solved.value())) {
362 values = std::move(unique->values);
363 } else {
364 auto& parametric =
365 std::get<detail::ParametricLinearSolution>(solved.value());
366 values = parametric.particular;
367 for (std::size_t parameter = 0;
368 parameter < parametric.free_columns.size(); ++parameter) {
369 auto free_variable = SymbolicExpr::variable(
370 variables[parametric.free_columns[parameter]]);
371 for (std::size_t column = 0; column < num_vars; ++column) {
372 auto term = SymbolicExpr::multiply(
373 parametric.nullspace_basis[parameter][column],
374 free_variable);
375 values[column] = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::add(
376 values[column], term)), transform_budget));
377 }
378 }
379 }
380
381 std::map<std::string, SymbolicExpr> solution;
382 for (std::size_t column = 0; column < num_vars; ++column) {
383 solution.insert_or_assign(
384 variables[column], *values[column]);
385 }
386 return solution;
387}
388
389
390} // namespace LMCAS
AssumptionContext class with scoped push/pop management.
幂运算节点,表示 base^exponent。
const std::shared_ptr< const SymbolicNode > & exponent() const noexcept
const std::shared_ptr< const SymbolicNode > & base() const noexcept
static std::shared_ptr< const SymbolicNode > create_add(std::vector< std::shared_ptr< const SymbolicNode > > ops, detail::RewriteBudget *budget=nullptr)
创建加法节点,自动扁平化嵌套加法并消除零项.
static std::shared_ptr< const SymbolicNode > create_variable(const std::string &name)
static std::shared_ptr< const SymbolicNode > create_power(std::shared_ptr< const SymbolicNode > base, std::shared_ptr< const SymbolicNode > exponent)
创建幂运算节点,自动处理指数为 0/1 及底数为 0/1 的情况.
static std::shared_ptr< const SymbolicNode > create_multiply(std::vector< std::shared_ptr< const SymbolicNode > > ops, detail::RewriteBudget *budget=nullptr)
创建乘法节点,自动扁平化嵌套乘法并消除单位元.
static std::shared_ptr< const SymbolicNode > create_number(const ::LMCAS::BigInt &v)
SymbolicNodePtr differentiate_node(const SymbolicNodePtr &node, const std::string &variable, 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
inline ::LMCAS::SymbolicExpr expression_from_node(SymbolicNodePtr root)
Result< ExactLinearSolution > solve_linear_exact(ExactMatrixData augmented, std::size_t coefficient_columns, ComputationContext &context, const std::string &operation)
std::shared_ptr< const Node > make_node(Args &&... args)
SymbolicNodePtr normalize_node(const SymbolicNodePtr &node, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
std::shared_ptr< SymbolicExpr > num(int value)
bool is_polynomial_node(const std::shared_ptr< const SymbolicNode > &node)
std::shared_ptr< SymbolicExpr > multiply_no_expand(const std::shared_ptr< const SymbolicNode > &term, const std::vector< std::shared_ptr< const SymbolicNode > > &denominator_factors)
std::map< std::string, SymbolicExpr > solve_linear_system_impl(const std::vector< SymbolicExpr > &equations, const std::vector< std::string > &variables, ComputationContext &context)
std::map< std::string, SymbolicExpr > project_unconditional_linear_system(const std::vector< SymbolicExpr > &equations, const std::vector< std::string > &variables, ComputationContext &context)
bool collect_denominator_factors(const std::shared_ptr< const SymbolicNode > &node, std::vector< std::shared_ptr< const SymbolicNode > > &denominator_factors, std::vector< std::shared_ptr< SymbolicExpr > > &denominator_constraints)
std::shared_ptr< SymbolicExpr > to_ptr(const SymbolicExpr &expression)
ExprResult pow(const ExprPtr &base, const ExprPtr &exponent, ComputationContext &context)
ExprResult exp(const ExprPtr &expression, ComputationContext &context)
__attribute__((visibility("default"))) ComplexSymbolicResult complex_exp_form_checked(std __attribute__((visibility("default"))) ComplexSymbolicResult complex_exp_form_checked(std __attribute__((visibility("default"))) ComplexSymbolicResult complex_trig_form_checked(std __attribute__((visibility("default"))) ComplexSymbolicResult complex_trig_form_checked(std __attribute__((visibility("default"))) ComplexRootsResult solve_complex_nth_root_checked(std __attribute__((visibility("default"))) ComplexRootsResult solve_complex_nth_root_checked(std __attribute__((visibility("default"))) ComplexRootsResult solve_complex_quadratic_checked(std __attribute__((visibility("default"))) ComplexRootsResult solve_complex_quadratic_checked(std std::shared_ptr< SymbolicExpr > r
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
static std::shared_ptr< const NumberNode > multiply_number_nodes(const std::shared_ptr< const NumberNode > &a, const std::shared_ptr< const NumberNode > &b)
ExprResult mul(const ExprPtr &lhs, const ExprPtr &rhs, ComputationContext &context)
ExprResult add(const ExprPtr &lhs, const ExprPtr &rhs, ComputationContext &context)
const MultiPoly & b
const ExprPtr RelationOp op
__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
static bool collect_power_denominators(const PowerNode &power, std::vector< std::shared_ptr< const SymbolicNode > > &den_factors, std::vector< std::shared_ptr< SymbolicExpr > > &den_constraints)
static std::pair< SymbolicExpr, SymbolicExpr > isolate_linear_coeff(const SymbolicExpr &expr, const std::string &var, ComputationContext &context)
const std::string const ExprPtr & value
static bool collect_operand_denominators(const std::vector< std::shared_ptr< const SymbolicNode > > &operands, std::vector< std::shared_ptr< const SymbolicNode > > &den_factors, std::vector< std::shared_ptr< SymbolicExpr > > &den_constraints)
static std::shared_ptr< const NumberNode > add_number_nodes(const std::shared_ptr< const NumberNode > &a, const std::shared_ptr< const NumberNode > &b)
static bool get_integer_value(const std::shared_ptr< const SymbolicNode > &node, BigInt &value)
多项式转换与精确符号多项式代数。
多项式方程组求解器 Solver,包含 Gröbner 基、线性系统求解。
bool operator()(const std::shared_ptr< const SymbolicNode > &a, const std::shared_ptr< const SymbolicNode > &b) const
内部 AST 定义。