I think this object would be useful, but not necessarily for the sort
of things suggested in this thread.
This would be useful for most of the common use-cases today of people
using evaluate=False, which usually amounts to making an expression
look a specific way, or keeping some numbers from combining. So for
instance if you are using SymPy to generate some step by step algebra
instructions and you want to be able to write something like x**2 +
2*x + 3 - 1 + 1 (say, for some completing the square instructions),
then this is useful. Given how prevalent questions about this sort of
thing are here and on StackOverflow, I think we definitely need
something like this, especially since evaluate=False has so many
issues.
However, for the idea being discussed here, the point is not just
about keeping numbers unevaluated but also computing with them. A much
more trivial way to keep numbers "unevaluated" is to use symbols with
numeric names, like Symbol('10')**Symbol('10')**Symbol('100'). That
also won't evaluate.
Say you want to use SymPy to try to figure out, for instance if
10**10**100 > E**E**E**E**E. A good start would be to take logs of
both sides, since log lets you pull out exponents, and it is
monotonic. You need some way of representing those numbers that works
with SymPy functions (like log), but absolutely never tries to
compute. If some function accidentally calls evalf on the wrong
subexpression it will raise an exception (best case) or hang the
computer (worst case).
Just using Pow(10, Pow(10, 100, evaluate=False), evaluate=False) is
bad because evaluate=False expressions are so fragile. Your
Unevaluated won't ever evaluate, but also SymPy functions won't know
what to do with it (e.g., log(Unevaluated(Pow, 2, 3)) won't simplify
to 3*log(2), even if you pass it through simplification functions like
expand_log(), because they won't recognize the Unevaluated as a Pow.
You would need to modify them. Conversely, if we used some
metaprogramming magic to make Unevaluated look like a Pow, then
existing functions might do some manipulations to it which would
effectively evaluate it, or at the very least, try to numerically
evaluate it, which is a no-no for our big number examples. Finally,
log(Symbol('10')**Symbol('10')**Symbol('100')) is no good because the
symbols don't have the right assumptions on them. Symbol('10',
even=True, positive=True) would get you closer.
My idea is to have a BigNumber wrapper, which pulls assumptions from
its arguments automatically. BigNumber(10).is_positive would be True.
But importantly, BigNumber(10).is_number would be False, and
BigNumber(10).evalf() would be a noop. There could also be a
big_number_simp() which would cautiously go through an expression and
remove BigNumber wrappers when they are no longer needed. For
instance, if you simplify
log(BigNumber(10)**BigNumber(10)**BigNumber(100)) to
BigNumber(10)**BigNumber(100)*log(BigNumber(10)) all the BigNumbers
can be removed (and 10**100 evaluated, since it isn't that big).
So, in short, I think both ideas are useful, but for different applications.
Aaron Meurer
>
https://groups.google.com/d/msgid/sympy/00f00329-a754-40dd-a4d3-d8e3be3a156a%40googlegroups.com.