|
1 | 1 | # -*- coding: utf-8 -*- |
2 | 2 |
|
3 | 3 | """ |
4 | | -arithmetic-related evaluation functions. |
| 4 | +helper functions for arithmetic evaluation, which do not |
| 5 | +depends on the evaluation context. Conversions to Sympy are |
| 6 | +used just as a last resource. |
5 | 7 |
|
6 | 8 | Many of these do do depend on the evaluation context. Conversions to Sympy are |
7 | 9 | used just as a last resource. |
@@ -320,6 +322,28 @@ def eval_complex_sign(n: BaseElement) -> Optional[BaseElement]: |
320 | 322 | return sign or eval_complex_sign(expr) |
321 | 323 |
|
322 | 324 |
|
| 325 | +def eval_Sign_number(n: Number) -> Number: |
| 326 | + """ |
| 327 | + Evals the absolute value of a number. |
| 328 | + """ |
| 329 | + if n.is_zero: |
| 330 | + return Integer0 |
| 331 | + if isinstance(n, (Integer, Rational, Real)): |
| 332 | + return Integer1 if n.value > 0 else IntegerM1 |
| 333 | + if isinstance(n, Complex): |
| 334 | + abs_sq = eval_add_numbers( |
| 335 | + *(eval_multiply_numbers(x, x) for x in (n.real, n.imag)) |
| 336 | + ) |
| 337 | + criteria = eval_add_numbers(abs_sq, IntegerM1) |
| 338 | + if test_zero_arithmetic_expr(criteria): |
| 339 | + return n |
| 340 | + if n.is_inexact(): |
| 341 | + return eval_multiply_numbers(n, eval_Power_number(abs_sq, RealM0p5)) |
| 342 | + if test_zero_arithmetic_expr(criteria, numeric=True): |
| 343 | + return n |
| 344 | + return eval_multiply_numbers(n, eval_Power_number(abs_sq, RationalMOneHalf)) |
| 345 | + |
| 346 | + |
323 | 347 | def eval_mpmath_function( |
324 | 348 | mpmath_function: Callable, *args: Number, prec: Optional[int] = None |
325 | 349 | ) -> Optional[Number]: |
@@ -347,6 +371,31 @@ def eval_mpmath_function( |
347 | 371 | return call_mpmath(mpmath_function, tuple(mpmath_args), prec) |
348 | 372 |
|
349 | 373 |
|
| 374 | +def eval_Exponential(exp: BaseElement) -> BaseElement: |
| 375 | + """ |
| 376 | + Eval E^exp |
| 377 | + """ |
| 378 | + # If both base and exponent are exact quantities, |
| 379 | + # use sympy. |
| 380 | + |
| 381 | + if not exp.is_inexact(): |
| 382 | + exp_sp = exp.to_sympy() |
| 383 | + if exp_sp is None: |
| 384 | + return None |
| 385 | + return from_sympy(sympy.Exp(exp_sp)) |
| 386 | + |
| 387 | + prec = exp.get_precision() |
| 388 | + if prec is not None: |
| 389 | + if exp.is_machine_precision(): |
| 390 | + number = mpmath.exp(exp.to_mpmath()) |
| 391 | + result = from_mpmath(number) |
| 392 | + return result |
| 393 | + else: |
| 394 | + with mpmath.workprec(prec): |
| 395 | + number = mpmath.exp(exp.to_mpmath()) |
| 396 | + return from_mpmath(number, prec) |
| 397 | + |
| 398 | + |
350 | 399 | def eval_Plus(*items: BaseElement) -> BaseElement: |
351 | 400 | "evaluate Plus for general elements" |
352 | 401 | numbers, items_tuple = segregate_numbers_from_sorted_list(*items) |
@@ -645,8 +694,58 @@ def eval_Times(*items: BaseElement) -> BaseElement: |
645 | 694 | ) |
646 | 695 |
|
647 | 696 |
|
| 697 | +def associate_powers(expr: BaseElement, power: BaseElement = Integer1) -> BaseElement: |
| 698 | + """ |
| 699 | + base^a^b^c^...^power -> base^(a*b*c*...power) |
| 700 | + provided one of the following cases |
| 701 | + * `a`, `b`, ... `power` are all integer numbers |
| 702 | + * `a`, `b`,... are Rational/Real number with absolute value <=1, |
| 703 | + and the other powers are not integer numbers. |
| 704 | + * `a` is not a Rational/Real number, and b, c, ... power are all |
| 705 | + integer numbers. |
| 706 | + """ |
| 707 | + powers = [] |
| 708 | + base = expr |
| 709 | + if power is not Integer1: |
| 710 | + powers.append(power) |
| 711 | + |
| 712 | + while base.has_form("Power", 2): |
| 713 | + previous_base, outer_power = base, power |
| 714 | + base, power = base.elements |
| 715 | + if len(powers) == 0: |
| 716 | + if power is not Integer1: |
| 717 | + powers.append(power) |
| 718 | + continue |
| 719 | + if power is IntegerM1: |
| 720 | + powers.append(power) |
| 721 | + continue |
| 722 | + if isinstance(power, (Rational, Real)): |
| 723 | + if abs(power.value) < 1: |
| 724 | + powers.append(power) |
| 725 | + continue |
| 726 | + # power is not rational/real and outer_power is integer, |
| 727 | + elif isinstance(outer_power, Integer): |
| 728 | + if power is not Integer1: |
| 729 | + powers.append(power) |
| 730 | + if isinstance(power, Integer): |
| 731 | + continue |
| 732 | + else: |
| 733 | + break |
| 734 | + # in any other case, use the previous base and |
| 735 | + # exit the loop |
| 736 | + base = previous_base |
| 737 | + break |
| 738 | + |
| 739 | + if len(powers) == 0: |
| 740 | + return base |
| 741 | + elif len(powers) == 1: |
| 742 | + return Expression(SymbolPower, base, powers[0]) |
| 743 | + result = Expression(SymbolPower, base, Expression(SymbolTimes, *powers)) |
| 744 | + return result |
| 745 | + |
| 746 | + |
648 | 747 | def eval_add_numbers( |
649 | | - *numbers: Number, |
| 748 | + *numbers: List[Number], |
650 | 749 | ) -> BaseElement: |
651 | 750 | """ |
652 | 751 | Add the elements in ``numbers``. |
@@ -693,7 +792,7 @@ def eval_inverse_number(n: Number) -> Number: |
693 | 792 | return eval_Power_number(n, IntegerM1) |
694 | 793 |
|
695 | 794 |
|
696 | | -def eval_multiply_numbers(*numbers: Number) -> Number: |
| 795 | +def eval_multiply_numbers(*numbers: Number) -> BaseElement: |
697 | 796 | """ |
698 | 797 | Multiply the elements in ``numbers``. |
699 | 798 | """ |
|
0 commit comments