Normally, plot2d has no problem dealing with errors:
plot2d('(if sin(i)<0 then i else log(-i)),[i,0,10])
expr: 0; plot2d('(if sin(i)<0 then i else log(expr)),[i,0,10])
They warn that they encountered log(0), but still display a plot with gaps.
But these produce no graphs at all:
plot2d('(if sin(i)<0 then i else log(0)),[i,0,10])
plot2d('(if sin(i)<0 then i else log(i-i)),[i,0,10])
Apparently plot2d is pre-evaluating/simplifying part of the expression.
Another problematic case (which was actually my original problem).
This produces a nice, helpful graph:
expr:sin(1)^2+cos(1)^2-1;
sc(x):=block([fpprec:floor(x)],float(log(1e-100+abs(bfloat(expr)))/log(10.0)));
plot2d(sc,[i,5,100]);
But this gives nonsense:
expr:sin(1)^2+cos(1)^2-1;
sc(x):=block([fpprec:floor(x)],float(log(1e-100+abs(bfloat(''expr)))/log(10.0)));
plot2d(sc,[i,5,100]);
Apparently it is evaluating the expr using float() in the global context.
Maxima 5.49.0 SBCL 2.5.1
This isn't related to
plot2d:The expression is simplified, and the
logsimplifier then encounters the zero argument.Is this a bug?
Last edit: David Scherfgen 6 days ago
Good point! Silly of me to overlook that!
Indeed,
'(if false then 1/0)gives an error because the arguments toifarebeing simplified even if the expression is not evaluated. This feels wrong,
even if I understand what is happening. I would be happier if it simplified
to
'(if false then 1/0). That should, I think, be a straightforwardchange to
simp-mcond-- if a clause causes an error while simplifying,just keep it as is.
The other case given in the example is worse. It seems that the simplifier
is called on arguments to
block, which means that any bindings whichaffect simplification are ignored, for example,
block([fpprec:21], 1+1b-20-1)simplifies toblock([fpprec:100], 0.0b0)rather thanblock([fpprec:21], 1b-20). Similarly for, say,dotassoc. This contrasts withlambda,which doesn't look at its contents at all:
'(lambda([fpprec],1+1b-20-1)(21)), which I'd think should denoteexactly the same as the
blockexpression.Much more broadly than plotting, this means that we can't manipulate Maxima
code as data.
Last edit: Robert Dodier 2 hours ago
This is what Claude has to say:
The
''exprcase isn't caused by block simplification. plot2d callsscwithnumer:truebound (coerce-maxima-function-or-maxima-lambdain plot.lisp). With''exprthe body containssin(1)andcos(1)literally, andnumerturns them into doubles beforebfloatsees them. Withbfloat(expr)the stored value ofexprcomes back unchanged, sonumernever touches it. Thefpprecbinding works fine, since:=stores the body unsimplified andblockbinds before evaluating it.Workaround:
block([numer:false, fpprec:floor(x)], ...). quad_qags, find_root etc. share this code, so dropping thenumerbinding would change them too. The plot2d documentation should at least mention it.Your point about block as data is right, but it's a separate issue.
blockhas no simplifier of its own, sosimpargssimplifies its body in the global environment.lambda,:=and::=have simplifiers that leave the body alone. Hence'(block([fpprec:21], 1+1b-20-1))givesblock([fpprec:21], 0.0b0), anddefine(g(), '(block(...)))defines a different function thang() := block(...). The same goes for(a, b),do,ifanderrcatch.Giving
blocka simplifier likesimplambdafixes it. The only new failure in the core and share tests is rtest_pytranslate problem 41, whose expected output relies on the quoted block being simplified (c+1+2+3becomes6+c). Does that sound like the right direction?simp-mcondonly exists in share/contrib/boolsimp. Coreifgoes throughsimpargslikeblock, but leaving its arguments alone is trickier, because anifwith an undecidable condition relies on the simplifier to tidy its branches.The big picture here is that both
ifandplot2d(via COERCE-FLOAT-FUN) have a certain amount of do-what-I-mean heuristics built into them. At this point I think I am inclined to open separate tickets forifandplot2d/ COERCE-FLOAT-FUN and close this one.A few random observations.
(1) Stavros, it would help if you say what's the expected output from the plotting example with the function
sc.(2) Does
makelistproduce the expected result, e.g.makelist(sc(i), i, 10, 20)? (I ask becausemakelistalso has nonstandard evaluation and I wonder what it does with this example.)(3) The nonstandard evaluation and simplification of
ifimplements some choices I made last time I worked on that stuff (maybe about 20 years ago). At the time I tried to make it work better as an unevaluated conditional. (It was already nonstandard, I just adjusted stuff.) Whatever I did (plus whatever came before and after) is certainly open to revision. I think I left some comments in the code about it.(4) COERCE-FLOAT-FUN tries pretty hard to get to a float. I guess the problem with
scinplot2dis that it's trying slightly too hard. We will have to look into details.(5) Incidentally, there is also a COERCE-BFLOAT-FUN, I think it's called from bigfloat versions of some numerical functions.
(6) I find the stuff from Claude about
''exprpretty frustrating. Just a long meandering text blob; I find myself wondering what, exactly, is the point. The problems that we're talking about are the consequences of design decisions -- any solution has to start there. It's not useful to start in a lengthy digression about something or the other and then wander further off the track. I know you, David, are finding Claude helpful, that's great. I just don't have the patience to try to get it back on track. /rant over.