Calling forget on F(%phi, constant) gives a stack overflow:
(%i1) forget(F(%phi, constant));
INFO: Caught stack overflow exception (sp=0x0000000000412000); proceed with caution.
Maxima encountered a Lisp error:
Control stack exhausted (no more space for function call frames).
This is probably due to heavily nested or infinitely recursive function
calls, or a tail call that SBCL cannot or has not optimized away.
PROCEED WITH CAUTION.
Automatically continuing.
To enable the Lisp debugger set *debugger-hook* to nil.
INFO: Reprotecting control stack guard (0x0000000000411000+0x21000)
Also, forget incorrectly forgets facts that are in the global context:
(%i1) constantp(%phi);
(%o1) true
(%i2) forget(kind(%phi, constant));
(%o2) [kind(%phi, constant)]
(%i3) constantp(%phi);
(%o3) false
Finally, the user-documentation for forget says that forget "Removes predicates established by assume." Facts in global aren't established by a user using assume, but internally, at least effectively, they are established by assume. So arguably, the user-documentation isn't clear.
Also, calling
forgeton a mapatom causes a stack overflow:A version of
forgetthat might fix these bugs:But the check
($member pat ($facts '$global))seems to require a noticeable amount of time and space.Tracing KILL2 seems to show that it is calling itself recursively. Something about
F(%phi, constant)makes KILL2 think that recursion is needed -- what is going on there? I notice that the presence of%phiseems to be key -- if it'sfoo, then the error is avoided.About refusing to forget facts in the context
global, I think I'm OK with removing those, and instead fixing up the documentation to say that built-in assertions inglobalcan be forgotten. In general Maxima doesn't apply the same operations to built-in stuff as to user-defined; looks like this is an area in which it does. I think that's generally a good idea -- applying rules uniformly makes it easier to predict the outcome.I would argue that allowing to forget facts such as that
%piis a constant or the newly addedzeroa > 0,zerob < 0is basically never a good idea and creates inconsistent and surprising results.%pilike a constant or having a certain value, regardless of whether the factkind(%pi, constant)exists in the database or not.%piand other system constants, so it's only consistent to also forbid making or removing assumptions about them.killrestores the initial assumptions on%pietc. So allowing to forget that%piis a constant and then callingkill(%pi)would restore the initial assumptions, which is very surprising. One wouldn't expectkillto actually add assumptions, only remove them.forgetcall. In fact, a similar thing happened inrtest_limit_extra.mac, which contained aassume(zeroa > 0)followed by some tests and thenforget(zeroa > 0). After these facts were promoted to the global context, this test (inadvertently) removed them.DATAproperty, which makes the fact effectively forgotten.If we do forbid modification of the
globalcontext (which I support), then we can makeconstantandmaxima-constantpfaster by first checking a symbol'ssysconstproperty before querying the database. Both functions are heavily used inside the simplifier. This reduceskindpcalls (expensive when the symbol carries facts, e.g. the built-in constants) by 20% while running the test suite.I think that no user‑level function should be allowd to modify
global. Allowing user code to alter the global context eliminates the need for both theinitialandglobalcontexts.The crash no longer happens, and the
globalcontext is now protected as of commit [cdc5f9].Related
Commit: [cdc5f9]