test: [ {a,rat(a)} , {rat(a),a} ] => [ {a}, {a} ]
is(test[1]=test[2]) => false
apply('set,test) => { {a}, {a} }
union(test[1],test[2]) => {a} << a
union(test[2],test[1]) => {a} << rat(a)
But...
is(a=rat(a)) => true
?print(test)$
((MLIST SIMP)
(($SET SIMP) $A)
(($SET SIMP) ((MRAT SIMP ($A) (#:A160)) (#:A160 1 1) . 1))
)
Not sure this is a bug.
In any case, not a high-priority one.
I think it's a bug; "=" for sets should be defined in terms of "=" for elements, so if elements are equivalent under "=", then so are the sets, I think. It appears that "=" for the sets is not punting to "=" for the elements as I would expect. I don't know how that would happen -- I seem to recall ALIKE1 for nonatomic expressions calls ALIKE1 for the operator and for each pair of arguments.
I haven't looked at ALIKE1 for some time and I haven't further investigated this bug, so I may be wildly mistaken about the whole thing, if so I apologize in advance.
I am on my phone only for another week, so can't look into this in detail.
But if you trace alike1, you'll see that rat(a} has been disrepped by the
time it gets to alike1 in is/=, setify, and for that matter sort. So it
can't distinguish them.
On Fri, Oct 2, 2026, 21:22 Robert Dodier via Maxima-bugs maxima-bugs@lists.sourceforge.net wrote:
Related
Bugs: #4033
I let Claude have a look at this. Stavros is right for a top-level CRE:
LIKE, whichis/=uses, converts it before callingALIKE1. Inside a set or list, though, the MRAT reachesALIKE1untouched. Tracingis({a} = {rat(a)})shows(ALIKE1 $A <MRAT>)returningNIL, sinceALIKE1never treats an MRAT as alike to a general expression.sortcan't tell them apart becauseorderlesspconverts at every depth.A small patch makes
LIKEcompare nested CRE forms by value, so=for sets follows=for elements, as Robert expected.ALIKE1itself stays unchanged, since rat and taylor code rely on it. The test suite passes. Patch to follow.I have a patch for this. With it,
is(x = y), the set functions,member,deleteandassoctreat a CRE form like its general form at any depth. So{a, rat(a)}and{rat(a), a}are equal,is([a] = [rat(a)])andmember([a], [[rat(a)]])aretrue, andassoc(a, [rat(a) = 1])returns1. The test suite passes.But there's still the issue of ordering:
greatdoesn't order a CRE form nested inside another expression like its general form. Soelementp,adjoinanddisjoincan go wrong on set members containing CRE forms, and sums can sort their terms differently:Shall we change
greatto order a CRE form like its general form? This fixes all of the above at once. The change is small, the test suite passes, results without CRE forms are unchanged and there's no measurable slowdown, but it touches the core ordering function.How do you define great so that sort([a, rat(a), a]) produces a
predictable result?
How do you distinguish rat(a+b,a) from rat(a+b,b)? They display
differently, but disrep to the same thing.
I'd think that is(a=rat(a)) should be false, just as is(0=0.0) is false,
and T[a] should be distinct from T[rat(a)].
On Sat, Oct 3, 2026, 07:43 David Scherfgen via Maxima-bugs maxima-bugs@lists.sourceforge.net wrote:
Related
Bugs: #4033
Behavior on current Maxima, before any patch:
sortwith its default order first converts the elements to general form and then uses a stable sort. Sosort([a, rat(a), a])returns[a, a, a], all in general form, and a change togreatwouldn't affect it. Ingreatitself,aandrat(a)would simply tie, which is howorderlesspalready treats them.rat(a+b, a)andrat(a+b, b)are already treated as equal, also inside lists and sets:is(rat(a+b, a) = rat(a+b, b))andis([rat(a+b, a)] = [rat(a+b, b)])aretrue, and{rat(a+b, a), rat(a+b, b)}has one member.ALIKE1compares two CRE forms by their general forms. The patch doesn't change this.is(a = rat(a))istrue, and the manual's introduction to sets relies on it: "Set members x and y are equal provided is(x = y) evaluates to true. Thus rat(x) and x are equal as set members", with{x, rat(x)}giving{x}.rtestnsettests this, for example withset(x, rat(x), x, x, x)andelementp(rat(x), set(x)). Also,f(rat(a))simplifies tof(a).T[rat(a)] : 1is an error ("hash function: cannot hash a special expression"), so hashed arrays never mix the two.So current Maxima treats a CRE form as another representation of the same expression. The bug is that
=follows this design only at the top level. The patch makes it hold at any depth.If we'd rather have
is(a = rat(a))befalse, the consistent fix goes the other way.=andALIKE1would compare CRE forms structurally, which would also separaterat(a+b, a)fromrat(a+b, b), and{a, rat(a)}would have two members. The manual and the tests above would change. That's a bigger change in behavior, but also a coherent one.Which way do we want?
I am surprised about the cases you mention. I thought I remembered the opposite, and specifically I think Barton and I discussed this when we were implementing the set package. I may be remembering incorrectly, but maybe someone could run a git bisect to see if it changed at some point?
Fundamentally, Maxima the programming language (independently of symbolic calculations) needs to have some primitive for checking whether two things are the same value (like equal in Lisp). This is different from whether they are the same object (eq in Lisp, which Maxima does not offer) or whether they have the same mathematical value (like equal in Maxima).
The basic axiom is that
a=bimpliesf(a)=f(b)for allf. The simplest violation using the disrep approach israt(x+y,x)vs.rat(x+y,y), forf=string, which are different. A more elaborate example involvesx+1, rat(x+1), taylor(x+1,x,0,1), andtaylor(x+1,x,0,5). Should they all be=? Certainly they give different values forf=taylorinfo. I suppose you could excuse that as some sort of utility function that cheats by peeking at internal representations. But they actually behave differently in substantive ways:taylor(exp(x),x,0,5)-taylor(x,x,0,1)gives a different result fromtaylor(exp(x),x,0,5)-taylor(x,x,0,5), so the basic definition is violated forf=taylor.Different but mathematically equal CRE expressions sometimes disrep to the same thing (
rat(a+b,a)vs.rat(a+b,b)) but in other cases they do not (rat(a*b+a,a) => (b+1)*a; rat(a*b+a,b) => a*b+a). That also seems problematic; how do we explain to the user that some things that look different are in fact=, but others are not?Now, we could define
=as "equal under disrep" (with all the issues I mention above), but in that case we'll need a parallel set of functions (=, setify, sort, member, ...) which actually use real equal for cases where it matters (or do we have a globaluse_strict_equal-- yuck!). That seems like unnecessary complexity.I let Claude check the history. It hasn't changed:
=has converted a top-level CRE form to general form at least since the oldest commit in the repository, the Maxima 5.4 sources (2000):LIKEisALIKE1afterSPECREPCHECK.set(x, rat(x))giving{x}. Its "Bugs" section blamedis([rat(a)]=[rat(a)])beingfalseonlike, not on the set code.orderlesspconverts CRE forms at every depth (2010),ALIKE1compares two CRE forms by value (2012), and the "Bugs" section was removed because its examples worked (2016).Your examples are all correct, and they all behave that way today, before any patch.
stringandtaylorinfodistinguish values that are=, Taylor truncation matters in arithmetic, andrat(a*b+a, a)andrat(a*b+a, b)are not=whilerat(a+b, a)andrat(a+b, b)are. The patch changes none of that. It only makes=treat a CRE form below the top level the way it already does at the top level.A strict
=is coherent, but existing code relies on the current one. Claude prototyped two strict versions and ran every test file:=to a general form, 7 test files crash with unbounded recursion, for example innusum, whose helperclistrecurses until0 = pwithpa CRE.gcdexdivides by zero,raddeneststops denesting, 65 of 210orthopolytests fail, and the documented set behavior changes ({x, rat(x)},subsetp({rat(x)}, {x})).So going strict means auditing and fixing such code (but what about foreign code that relies on this?) and changing the manual.
Thanks for doing all that work just to correct my memory!
I still think it's problematic that
a=bdoes not implyf(a)=f(b)for allf, but pragmatically, I agree that staying with the status quo makes sense.Well, it was Claude that did the digging.
Then shall I go ahead with the patch that makes
=,member,delete,assocand the set functions treat a CRE form like its general form at every depth instead of just at the top level?I'd also change
greatthe same way. That's the same rule applied to ordering, and it fixeselementp,adjoin,disjoinand the term order of sums at once. I'd also add a note to the documentation of=that a CRE form and its general form are=, while functions that see the representation, such asstringandtaylorinfo, can still tell them apart.