Re: Inconsistencies and special treatment of %e vs. %pi (and numer, %enumer mess)
Stavros Macrakis <[email protected]> Tue, 9 Jun 2026 16:15:23 -0400
| Newsgroups | gmane.comp.mathematics.maxima.general |
|---|---|
| Message-ID | <CACLVabUqp2-DQ6g5Y2JiGkkcVuZGxLBc5xoopM2GfQq-LxQonw@mail.gmail.com> |
Re *%pi^x*, I don't think that is ever really used in math, and so no one cares. Understood about idea vs. implementation, but I don't much like the idea of having mathematical transformations performed in evaluation. I've been trying for years to wean people off of the overuse of *ev*, and this gives a new reason to use *ev*. As far as I'm concerned, it should be possible to perform *all* mathematical operations without involving evaluation at all. We're not there yet -- for example, *nouns* today is bound up with evaluation (*ev(...,nouns,noeval)* doesn't replace noun forms with verb forms) -- but I'd like to move in that direction. On Tue, Jun 9, 2026 at 3:53 PM David Scherfgen <[email protected]> wrote: > Interesting observation regarding *bfloat* vs. *float*. > > As I said, I agree with the idea of leaving *%e^x* alone even if > *numer=true* unless *x* is numeric. > > (By the way, wouldn't that also apply to *%pi^x*? Is it ever desirable to > rewrite that as *3.141^x* unless *x* is purely numeric?) > > My main point is that while the *idea* is good, the current > implementation is incorrect (see examples I posted) and relies on lots of > intertwined special-case code. What I find the most "disturbing" is that > the *simplifier* turns *%e* into *2.718*, making it the only atom that > the simplifier can modify. This is bad. One would think that an atom is > inherently simplified, and it cannot have a *simp* tag. > > What if we rip out all the special treatment of *%e* in the *simplifier* > and leave it to the *evaluator* to replace it with its numeric value? > Then we can try to make *%enumer* work in the evaluator, i.e. prevent > *%e^x* with symbolic *x* from becoming *2.718^x*. > > > Stavros Macrakis <[email protected]> schrieb am Di., 9. Juni 2026, 20:57: > >> My guess is that the purpose of special-casing *%e^x* is that people >> think of it as a notation for the function *exp(x)*. It is ever >> desirable to rewrite *%e^x* as *2.718^x *unless *x* is purely numeric? >> There is nothing analogous for *%pi *or other constants. >> >> By the way, I see that *bfloat(...)* preserves symbolic *%e *when it is >> the base of an exponentiation, because it does not work via simplification. >> *float(...)*, on the other hand, does not preserve it. >> >> I'll also note that *xxx,float == ex(xxx,float)* is equivalent to >> binding *float:true*, whereas *xxx,bfloat == ex(xxx,bfloat)* is >> equivalent to *bfloat(xxx)*... which in most cases results in similar >> results. But I wouldn't dare to make them consistent because I don't know >> how much user code it would break. >> >> On Tue, Jun 9, 2026 at 1:10 PM David Scherfgen via Maxima-discuss < >> [email protected]> wrote: >> >>> Dear all, >>> >>> First, please excuse the rather long mail. If it's too long for you to >>> read it completely, please read only the example codes that show the >>> problem, and then skip to "TLDR / Way forward?". >>> >>> While digging into the simplifier and evaluator, I noticed that %e >>> receives an almost ridiculous amount of special treatment compared to, >>> e.g., %pi. >>> >>> - %e is the *only atom* that can be simplified to something >>> different by the simplifier: If the global flags numer and %enumer >>> are both true, %e is simplified to its numerical value. It's likely >>> that there is code at other places that (wrongly, but understandably) >>> assumes that atoms are inherently simplified and don't need to go through >>> the simplifier, which would cause bugs. The other constants like %pi >>> aren't handled by the simplifier. >>> - There is some really ugly hackery going on in the evaluator that >>> explicitly targets %e in conjunction with numer/%enumer. >>> >>> The global flag %enumer = false (default) is supposed to "protect" %e >>> from becoming a float when numer is true in cases of %e^x, where x is >>> symbolic. At first glance, this works: >>> >>> (%i1) %e^x + sin(%e), numer; >>> (%o1) %e^x + 0.41078129050290885 >>> >>> Here, %e^x is correctly left alone, while sin(%e) becomes a float. >>> The following also works: >>> >>> (%i2) sin(%e^x), numer; >>> (%o2) sin(%e^x) >>> >>> But now let's resimplify that with numer on (classically, this would be >>> done with expand(%, 0, 0), but we have resimplify now): >>> >>> (%i3) resimplify(%), numer; >>> (%o3) sin(2.718281828459045^x) >>> >>> Oops! Now %e became a float, even though it's the base with a symbolic >>> exponent, which is exactly the case where it should have been "protected" >>> by %enumer = false. >>> >>> What's going on here? >>> >>> - When typing just sin(%e^x), numer in the REPL, the evaluator >>> starts by simplifying the leaves %e and x, then builds and >>> simplifies %e^x, and finally builds and simplifies sin(%e^x). It >>> works its way from the leaves to the root, *bottom-up*. >>> - But when we resimplify, all simp tags are stripped from the entire >>> expression, and the simplifier is called on the root, working in a >>> *top-down* fashion. The sin simplifier calls simpcheck on its >>> argument, which, due to a momentous commit described here >>> <https://sourceforge.net/p/maxima/bugs/123/>, dynamically binds >>> %enumer to numer (true). This "infects" the entire sub-tree, and all >>> instances of %e inside it are converted to a float by the >>> simplifier, no matter what. >>> >>> So we get a different outcome depending on whether we work bottom-up or >>> top-down. It doesn't end there, though. Here's an example that shows how >>> %e and %pi are treated in a fundamentally different way: >>> >>> /* OK, both become floats: */ >>> (%i4) %pi*y + %e*x, numer; >>> (%o4) 3.141592653589793*y + 2.718281828459045*x >>> >>> /* Not OK, %e stays symbolic: */ >>> (%i5) [%pi, %e], numer; >>> (%o5) [3.141592653589793, %e] >>> >>> /* If we write -%e instead of %e, it becomes a float: */ >>> (%i6) [%pi, -%e], numer; >>> (%o6) [3.141592653589793, -2.718281828459045] >>> >>> *TLDR / Way forward?* >>> >>> - In summary, the handling of %e, numer and %enumer is an >>> inconsistent mess that needs lots of special cases in the code, and it >>> doesn't even work as intended. >>> - I think that, as much as possible, this extra treatment of %e >>> should go away. It would make the code simpler and the results more >>> consistent. >>> - The idea behind %enumer = false isn't bad, but there's a >>> fundamental problem with the way it's supposed to work: When working >>> bottom-up, we first see %e, but *we don't have the context*, i.e. we >>> don't know where that %e sits inside a parent expression (if any). >>> - Is it the %e in %e^x? Then it must not become a float. >>> - Is it any %e in %e^%e? Then it must become a float. >>> - Is it the %e in sin(%e)? Then it must become a float. >>> - Is it just a standalone %e? Then it must become a float. >>> - There's one way it could work, although it comes with a catch: >>> - We stop handling %e in simplifya entirely. *Nice side effect: >>> Atoms are always simplified!* >>> - Instead, we treat %e like any other constant, e.g. %pi, and let >>> the evaluator convert it to float unconditionally when numer is >>> true. >>> - Afterwards, if %enumer is false, we look explicitly for >>> 2.718281828459045^(symbolic) and convert that back to >>> %e^(symbolic). Note that this is an exact comparison to %e-val >>> that will not be affected by floating point issues. >>> - The catch: If the user manually enters 2.718281828459045^x, it >>> will become %e^x. Maybe that's acceptable? I don't see any other >>> way of achieving the desired behavior. >>> - Alternatively, we could degrade %enumer to a selective switch just >>> for %e. With %enumer = false, it would always prevent %e from >>> becoming a float when numer is true. But that would include things >>> like sin(%e), where the user would probably expect a float. This >>> behavior would probably not be very useful, and I don't like the idea of >>> changing the meaning of a flag in a significant way, as it would destroy >>> backwards-compatibility. >>> >>> Looking forward to the community's opinions on this. >>> >>> Best regards >>> David Scherfgen >>> _______________________________________________ >>> Maxima-discuss mailing list >>> [email protected] >>> https://lists.sourceforge.net/lists/listinfo/maxima-discuss >>> >> _______________________________________________ Maxima-discuss mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/maxima-discuss