LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
value.cpp
浏览该文件的文档.
2#include "value.hpp"
4#include "lmmc/numeric.h"
5#include <cmath>
6#include <stdexcept>
7
8namespace LMCAS {
9
10namespace {
11void append_array(std::string& result, const std::vector<Value>& values) {
12 result += "[";
13 for (size_t index = 0; index < values.size(); ++index) {
14 if (index) result += ", ";
15 result += values[index].to_string();
16 }
17 result += "]";
18}
19
20std::string format_array(const std::vector<Value>& values) {
21 std::string result;
22 append_array(result, values);
23 return result;
24}
25
26std::string format_matrix(const std::vector<std::vector<Value>>& matrix) {
27 std::string result = "[";
28 for (size_t row = 0; row < matrix.size(); ++row) {
29 if (row) result += ", ";
30 append_array(result, matrix[row]);
31 }
32 result += "]";
33 return result;
34}
35
36std::string format_float(lmmc_real_t value) {
37 std::string result = std::to_string(value);
38 result.erase(result.find_last_not_of('0') + 1, std::string::npos);
39 result.erase(result.find_last_not_of('.') + 1, std::string::npos);
40 return result;
41}
42
43const char* format_infinity(const int* sign) {
44 return sign && *sign > 0 ? "inf" : "-inf";
45}
46
47template <typename Exact>
49 const auto range_failure = [] {
52 "exact numeric value is outside the floating-point range",
53 "Value::as_number_checked");
54 };
55 try {
56 const lmmc_real_t converted = value.to_double();
57 if (!std::isfinite(converted) ||
58 (converted == 0.0 && !value.is_zero())) {
59 return range_failure();
60 }
62 } catch (const std::overflow_error&) {
63 return range_failure();
64 } catch (const std::underflow_error&) {
65 return range_failure();
66 } catch (const std::out_of_range&) {
67 return range_failure();
68 } catch (const std::range_error&) {
69 return range_failure();
70 }
71}
72}
73
74 Value::Value(lmmc_real_t f) : type(Type::Float), data(f) {
75 if (std::isnan(f)) {
76 throw std::invalid_argument("Value: floating-point value must not be NaN");
77 }
78 int res;
79 lmmc_isinf(f, &res);
80 if (res) {
81 if (f < 0) res = -1;
82 this->type = Type::Infinity;
83 this->data = DataType(std::in_place_index<1>, res);
84 }
85 }
86
87 Value::Value(const std::vector<Value>& arr) {
88 bool is_matrix = !arr.empty() && arr[0].is_array();
89 if (is_matrix) {
90 std::vector<std::vector<Value>> matrix;
91 for (const auto& row : arr) {
92 if (row.is_array()) {
93 matrix.push_back(std::get<std::vector<Value>>(row.data));
94 } else {
95 type = Type::Array;
96 data = arr;
97 return;
98 }
99 }
100 type = Type::Matrix;
101 data = validate_matrix(std::move(matrix));
102 } else {
103 type = Type::Array;
104 data = arr;
105 }
106 }
107
108 bool Value::is_numeric() const {
110 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
111 (context).limits().max_ast_nodes, "transform");
112 if (type == Type::Int || type == Type::Float ||
113 type == Type::BigInt || type == Type::Rational ||
114 type == Type::Irrational) {
115 return true;
116 }
117 if (type == Type::Symbolic) {
118 const auto& sp = std::get<std::shared_ptr<SymbolicExpr>>(data);
119 if (!sp) return false;
120 try {
121 auto simp = detail::make_expression_ptr(detail::normalize_node(detail::node(sp), transform_budget));
122 return simp && simp->is_number();
123 } catch (const std::invalid_argument&) {
124 return false;
125 } catch (const std::out_of_range&) {
126 return false;
127 }
128 }
129 return false;
130 }
131
132 LMCAS::Result<lmmc_real_t> Value::as_number_checked() const {
133 if (type == Type::Infinity) {
134 lmmc_real_t inf;
135 lmmc_inf(&inf);
136 const auto sign = std::get_if<int>(&data);
138 (sign && *sign > 0) ? inf : -inf);
139 }
140 if (type == Type::Int) {
142 static_cast<lmmc_real_t>(std::get<int>(data)));
143 }
144 if (type == Type::Float) {
146 std::get<lmmc_real_t>(data));
147 }
148 if (type == Type::BigInt) {
150 std::get<::LMCAS::BigInt>(data));
151 }
152 if (type == Type::Rational) {
154 std::get<::LMCAS::Rational>(data));
155 }
156 if (type == Type::Irrational) {
158 std::get<::LMCAS::Irrational>(data).to_double());
159 }
160 if (type == Type::Symbolic) {
161 const auto& expression =
162 std::get<std::shared_ptr<SymbolicExpr>>(data);
163 auto evaluated = LMCAS::evaluate_numeric(*expression);
164 if (!evaluated) {
166 evaluated.error());
167 }
168 if (!evaluated.value().is_finite() ||
169 !std::isfinite(evaluated.value().value)) {
172 "symbolic value did not evaluate to a finite number",
173 "Value::as_number_checked");
174 }
176 static_cast<lmmc_real_t>(evaluated.value().value));
177 }
180 "value is not numeric", "Value::as_number_checked");
181 }
182
183
184 LMCAS::Result<::LMCAS::Rational> Value::as_rational_checked() const {
185 if (type == Type::Rational) {
187 std::get<::LMCAS::Rational>(data));
188 }
189 if (type == Type::Int) {
191 ::LMCAS::Rational(std::get<int>(data)));
192 }
193 if (type == Type::Float) {
195 ::LMCAS::Rational::from_double(std::get<lmmc_real_t>(data)));
196 }
197 if (type == Type::BigInt) {
199 ::LMCAS::Rational(std::get<::LMCAS::BigInt>(data)));
200 }
201 if (type == Type::Irrational) {
202 return std::get<::LMCAS::Irrational>(data).as_rational_checked();
203 }
206 "value cannot be represented as a rational",
207 "Value::as_rational_checked");
208 }
209
210
211 LMCAS::Result<::LMCAS::Irrational> Value::as_irrational_checked() const {
212 if (type == Type::Irrational) {
214 std::get<::LMCAS::Irrational>(data));
215 }
216 if (type == Type::Int) {
218 ::LMCAS::Irrational::constant(::LMCAS::Rational(std::get<int>(data))));
219 }
220 if (type == Type::Float) {
222 ::LMCAS::Irrational::constant(std::get<lmmc_real_t>(data)));
223 }
224 if (type == Type::Rational) {
226 ::LMCAS::Irrational::constant(
227 std::get<::LMCAS::Rational>(data)));
228 }
229 if (type == Type::BigInt) {
231 ::LMCAS::Irrational::constant(
232 ::LMCAS::Rational(std::get<::LMCAS::BigInt>(data))));
233 }
236 "value cannot be represented as an irrational wrapper",
237 "Value::as_irrational_checked");
238 }
239
240
241 LMCAS::Result<std::shared_ptr<SymbolicExpr>> Value::as_symbolic_checked() const {
242 if (type == Type::Infinity) {
243 const auto sign = std::get_if<int>(&data);
244 return LMCAS::Result<std::shared_ptr<SymbolicExpr>>::success(
245 SymbolicExpr::infinity(sign ? *sign : 1));
246 }
247 if (type == Type::Symbolic) {
248 return LMCAS::Result<std::shared_ptr<SymbolicExpr>>::success(
249 std::get<std::shared_ptr<SymbolicExpr>>(data));
250 }
251 if (type == Type::Float) {
252 return LMCAS::Result<std::shared_ptr<SymbolicExpr>>::success(
253 SymbolicExpr::number(std::get<lmmc_real_t>(data)));
254 }
255 if (type == Type::Int || type == Type::Rational ||
256 type == Type::BigInt) {
257 auto rational = as_rational_checked();
258 if (!rational) {
259 return LMCAS::Result<std::shared_ptr<SymbolicExpr>>::failure(
260 rational.error());
261 }
262 return LMCAS::Result<std::shared_ptr<SymbolicExpr>>::success(
263 SymbolicExpr::number(rational.value()));
264 }
265 if (type == Type::Irrational) {
266 return LMCAS::Result<std::shared_ptr<SymbolicExpr>>::success(
267 std::get<::LMCAS::Irrational>(data).to_symbolic());
268 }
269 if (type == Type::Matrix) {
270 const auto& matrix =
271 std::get<std::vector<std::vector<Value>>>(data);
272 std::vector<std::vector<std::shared_ptr<SymbolicExpr>>>
273 symbolic_matrix;
274 symbolic_matrix.reserve(matrix.size());
275 for (const auto& row : matrix) {
276 std::vector<std::shared_ptr<SymbolicExpr>> symbolic_row;
277 symbolic_row.reserve(row.size());
278 for (const auto& value : row) {
279 auto symbolic = value.as_symbolic_checked();
280 if (!symbolic) {
281 return LMCAS::Result<
282 std::shared_ptr<SymbolicExpr>>::failure(
283 symbolic.error());
284 }
285 symbolic_row.push_back(std::move(symbolic.value()));
286 }
287 symbolic_matrix.push_back(std::move(symbolic_row));
288 }
289 return LMCAS::Result<std::shared_ptr<SymbolicExpr>>::success(
290 SymbolicExpr::matrix(symbolic_matrix));
291 }
292 return LMCAS::Result<std::shared_ptr<SymbolicExpr>>::failure(
294 "value cannot be converted to a symbolic expression",
295 "Value::as_symbolic_checked");
296 }
297
298
299 bool Value::as_symbolic_compatible() const {
300 switch (type) {
301 case Type::Symbolic: {
302 return true;
303 }
304 case Type::Infinity: {
305 return true;
306 }
307 case Type::Int: {
308 return true;
309 }
310 case Type::Float: {
311 return true;
312 }
313 case Type::Rational: {
314 return true;
315 }
316 case Type::BigInt: {
317 return true;
318 }
319 case Type::Irrational: {
320 return true;
321 }
322 case Type::Matrix: {
323 return true;
324 }
325 default:
326 return false;
327 }
328 }
329
330 std::string Value::to_string() const {
331 if (type == Type::String) {
332 return _str_cache;
333 }
334 if (!_str_cache.empty()) {
335 return _str_cache;
336 }
337 switch (type) {
338 case Type::Infinity: {
339 return format_infinity(std::get_if<int>(&data));
340 }
341 case Type::Null: {
342 return "null";
343 }
344 case Type::Int: {
345 return std::to_string(std::get<int>(data));
346 }
347 case Type::Float: {
348 return format_float(std::get<lmmc_real_t>(data));
349 }
350 case Type::Array: {
351 return format_array(std::get<std::vector<Value>>(data));
352 }
353 case Type::Matrix: {
354 return format_matrix(std::get<std::vector<std::vector<Value>>>(data));
355 }
356 case Type::BigInt: {
357 return std::get<::LMCAS::BigInt>(data).to_string();
358 }
359 case Type::Rational: {
360 return std::get<::LMCAS::Rational>(data).to_string();
361 }
362 case Type::Irrational: {
363 return std::get<::LMCAS::Irrational>(data).to_string();
364 }
365 case Type::Symbolic: {
366 const auto& expression =
367 std::get<std::shared_ptr<SymbolicExpr>>(data);
368 if (!expression) {
369 return "<invalid-symbolic>";
370 }
371 return expression->to_string();
372 }
373 default:
374 return "<unknown>";
375 }
376 }
377
378 bool Value::operator==(const Value& other) const {
379 if (type != other.type) return false;
380 if (type == Type::String) {
381 return _str_cache == other._str_cache;
382 }
383 if (data.index() != other.data.index()) return false;
384 return std::visit([&other](const auto& val1) -> bool {
385 using T = std::decay_t<decltype(val1)>;
386 if constexpr (std::is_same_v<T, std::nullptr_t>) {
387 return true;
388 } else if constexpr (std::is_same_v<T, std::shared_ptr<SymbolicExpr>>) {
389 if (!val1 && !std::get<T>(other.data)) return true;
390 if (!val1 || !std::get<T>(other.data)) return false;
391 return val1->compare(std::get<T>(other.data)) == 0;
392 } else {
393 return val1 == std::get<T>(other.data);
394 }
395 }, data);
396 }
397
398 bool Value::operator<(const Value& other) const {
399 if (type != other.type) return static_cast<int>(type) < static_cast<int>(other.type);
400 if (type == Type::String) {
401 return _str_cache < other._str_cache;
402 }
403 if (data.index() != other.data.index()) return data.index() < other.data.index();
404 return std::visit([&other](const auto& val1) -> bool {
405 using T = std::decay_t<decltype(val1)>;
406 if constexpr (std::is_same_v<T, std::nullptr_t>) {
407 return false;
408 } else if constexpr (std::is_same_v<T, std::shared_ptr<SymbolicExpr>>) {
409 if (!val1 && !std::get<T>(other.data)) return false;
410 if (!val1) return true;
411 if (!std::get<T>(other.data)) return false;
412 return val1->compare(std::get<T>(other.data)) < 0;
413 } else {
414 return val1 < std::get<T>(other.data);
415 }
416 }, data);
417 }
418
419 LMCAS::Result<Value> Value::vector_add_checked(const Value& other) const {
420 if (!is_array() || !other.is_array()) {
423 "vector addition requires two arrays",
424 "Value::vector_add_checked");
425 }
426 const auto& lhs = std::get<std::vector<Value>>(data);
427 const auto& rhs = std::get<std::vector<Value>>(other.data);
428 if (lhs.size() != rhs.size()) {
431 "vector lengths do not match",
432 "Value::vector_add_checked");
433 }
434 std::vector<Value> values;
435 values.reserve(lhs.size());
436 for (size_t index = 0; index < lhs.size(); ++index) {
437 auto left = lhs[index].as_number_checked();
438 if (!left) return LMCAS::Result<Value>::failure(left.error());
439 auto right = rhs[index].as_number_checked();
440 if (!right) return LMCAS::Result<Value>::failure(right.error());
441 values.emplace_back(left.value() + right.value());
442 }
443 return LMCAS::Result<Value>::success(Value(values));
444 }
445
446
447 LMCAS::Result<Value> Value::dot_product_checked(const Value& other) const {
448 if (!is_array() || !other.is_array()) {
451 "dot product requires two arrays",
452 "Value::dot_product_checked");
453 }
454 const auto& lhs = std::get<std::vector<Value>>(data);
455 const auto& rhs = std::get<std::vector<Value>>(other.data);
456 if (lhs.size() != rhs.size()) {
459 "vector lengths do not match",
460 "Value::dot_product_checked");
461 }
462 lmmc_real_t sum = 0.0;
463 for (size_t index = 0; index < lhs.size(); ++index) {
464 auto left = lhs[index].as_number_checked();
465 if (!left) return LMCAS::Result<Value>::failure(left.error());
466 auto right = rhs[index].as_number_checked();
467 if (!right) return LMCAS::Result<Value>::failure(right.error());
468 sum += left.value() * right.value();
469 }
470 return LMCAS::Result<Value>::success(Value(sum));
471 }
472
473
474 LMCAS::Result<Value> Value::matrix_multiply_checked(
475 const Value& other) const {
476 if (!is_matrix() || !other.is_matrix()) {
479 "matrix multiplication requires two matrices",
480 "Value::matrix_multiply_checked");
481 }
482 const auto& lhs =
483 std::get<std::vector<std::vector<Value>>>(data);
484 const auto& rhs =
485 std::get<std::vector<std::vector<Value>>>(other.data);
486 if (lhs.empty() || rhs.empty() ||
487 lhs.front().size() != rhs.size()) {
490 "matrix dimensions are not multiplicatively compatible",
491 "Value::matrix_multiply_checked");
492 }
493 const size_t rows = lhs.size();
494 const size_t columns = rhs.front().size();
495 const size_t inner = lhs.front().size();
496 std::vector<std::vector<Value>> values(
497 rows, std::vector<Value>(columns, Value(0.0)));
498 for (size_t row = 0; row < rows; ++row) {
499 for (size_t column = 0; column < columns; ++column) {
500 lmmc_real_t sum = 0.0;
501 for (size_t index = 0; index < inner; ++index) {
502 auto left = lhs[row][index].as_number_checked();
503 if (!left) {
504 return LMCAS::Result<Value>::failure(left.error());
505 }
506 auto right = rhs[index][column].as_number_checked();
507 if (!right) {
508 return LMCAS::Result<Value>::failure(right.error());
509 }
510 sum += left.value() * right.value();
511 }
512 values[row][column] = Value(sum);
513 }
514 }
515 return LMCAS::Result<Value>::success(Value(values));
516 }
517
518
519 std::shared_ptr<SymbolicExpr> Value::require_symbolic(
520 const std::shared_ptr<SymbolicExpr>& expression) {
521 if (!expression) {
522 throw std::invalid_argument(
523 "Value: symbolic expression must not be null");
524 }
525 return expression;
526 }
527
528 std::vector<std::vector<Value>> Value::validate_matrix(
529 std::vector<std::vector<Value>> matrix) {
530 if (matrix.empty()) return matrix;
531 const size_t columns = matrix.front().size();
532 for (const auto& row : matrix) {
533 if (row.size() != columns) {
534 throw std::invalid_argument(
535 "Value: all matrix rows must have the same number of columns");
536 }
537 }
538 return matrix;
539 }
540
541}
CasError & error()
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(T value)
std::string format_array(const std::vector< Value > &values)
LMCAS::Result< lmmc_real_t > exact_number_as_floating(const Exact &value)
void append_array(std::string &result, const std::vector< Value > &values)
std::string format_matrix(const std::vector< std::vector< Value > > &matrix)
std::string format_float(lmmc_real_t value)
const char * format_infinity(const int *sign)
Result< void > validate_matrix(const ExactMatrixData &matrix, const std::string &operation)
std::shared_ptr<::LMCAS::SymbolicExpr > make_expression_ptr(SymbolicNodePtr root)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
SymbolicNodePtr normalize_node(const SymbolicNodePtr &node, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
@ Rational
Rational numbers
const ExprPtr & right
const std::string const ExprPtr & value
const ExprPtr & rhs
Result< ApproxReal > evaluate_numeric(const SymbolicExpr &expression, const NumericBindings &bindings={})
ExprResult rational(const Rational &value)
运行时值类型 Value,支持数值,符号,容器类型.