sagemath / sage

Main repository of SageMath. Now open for Issues and Pull Requests.
https://www.sagemath.org
Other
1.21k stars 421 forks source link

RuntimeError when converting maxima object to symbolic ring #36770

Open DaveWitteMorris opened 7 months ago

DaveWitteMorris commented 7 months ago

Steps To Reproduce

Ask sage to evaluate the following:

n = var('n')
sum(1/(5*n) - 1/(5*n + 1), n, 2, oo)

Expected Behavior

Maxima is able to calculate this sum, so sage should return a reasonable answer.

Actual Behavior

RuntimeError: Encountered operator mismatch in maxima-to-sr translation

Additional Information

The problem was reported in this sage-devel thread. It may be related to #34028.

Diagnosis. The problem is in lines 1729-1732 of the max_to_sr method in src/sage/interfaces/maxima_lib.py:

                # This could be unsafe if the conversion to SR
                # changes the structure of expr
                sage_expr = SR(maxima(expr))
                op = sage_expr.operator()

In the above sum example, the answer calculated by maxima involves cot(pi/5). The conversion SR(maxima(expr)) simplifies this to the SR expression 1/5*sqrt(10*sqrt(5) + 25). So the operator in the maxima expression is cot but the main operator in the SR expression is multiplication. This mismatch results in a RuntimeError.

Here is a more direct illustration of the bug:

sage: from sage.interfaces.maxima_lib import maxima_lib, max_to_sr
sage: cotpiby5 = maxima_lib("cot(%pi/5)")
sage: cotpiby5.ecl()
<ECL: ((%COT SIMP) ((MTIMES SIMP) ((RAT SIMP) 1 5) $%PI))>
sage: SR(maxima(cotpiby5))
1/5*sqrt(10*sqrt(5) + 25)
sage: max_to_sr(cotpiby5.ecl())
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
Cell In [26], line 1
----> 1 max_to_sr(cotpiby5.ecl())

File ~/development/sage/src/sage/interfaces/maxima_lib.py:1734, in max_to_sr(expr)
   1732     op = sage_expr.operator()
   1733 if op in sage_op_dict:
-> 1734     raise RuntimeError("Encountered operator mismatch in maxima-to-sr translation")
   1735 max_op_dict[op_max] = op
   1736 sage_op_dict[op] = op_max

RuntimeError: Encountered operator mismatch in maxima-to-sr translation

Possible solution. The faulty lines are a hack to determine how the maxima operator should be translated to SR. Perhaps this could be fixed by providing an API to query SR more directly.

Environment

any operating system and recent sage version

Checklist

nbruin commented 7 months ago

If you teach it some translations first it doesn't run into problems later:

sage: integrate(cot(x),x)
log(sin(x))
sage: sage.interfaces.maxima_lib.max_to_sr(maxima_calculus(cot(x)).ecl())
cot(x)
sage: sage.interfaces.maxima_lib.max_to_sr(maxima_calculus(cos(x)).ecl())
cos(x)
sage: sage.interfaces.maxima_lib.max_to_sr(maxima_calculus(sin(x)).ecl())
sin(x)

so perhaps the fix is to prepopulate the translation dictionary a bit. These automatically evaluating expressions are a bit of a problem: why do we even get that if explicit construction automatically evaluates it?

DaveWitteMorris commented 7 months ago

Elements of SR are essentially pynac objects. I don't think it's surprising that the simplifications applied in constructing a pynac object are not exactly the same as the ones found in maxima.

To fix this, I think we would need to either prepopulate the translation dictionary extensively (not just a bit) or find a reliable way to query the translation dictionary that is used by the SR constructor.

EmmanuelCharpentier commented 7 months ago

FWIW :

sage: with assuming(n>0): maxima_calculus.sum(*map(maxima,(1/(25*n^2+5*n), n, 2, oo)))
inf
sage: giac.sum(*map(giac, (1/(25*n^2+5*n), n, 2, oo)))
infinity

But :

sage: mathematica("Sum[1/(5*n)-1/(5*n+1), {n, 2, Infinity}]")
(580 - 3*Sqrt[2]*(5 + Sqrt[5])^(3/2)*Pi - 10*Log[30517578125] + 
  30*Sqrt[5]*Log[5 - Sqrt[5]] - 30*Sqrt[5]*Log[5 + Sqrt[5]])/600
sage: mathematica("FullSimplify[Sum[1/(5*n)-1/(5*n+1), {n, 2, Infinity}]]")
(290 - 6*Sqrt[5*(5 + 2*Sqrt[5])]*Pi - 30*Sqrt[5]*ArcCoth[Sqrt[5]] - 
  75*Log[5])/300

The problem may be elsewhere...

DaveWitteMorris commented 7 months ago

It looks like there may be other problems too, but I am confident that the RuntimeError in the original sum is directly caused by the operator mismatch that I mentioned above, resulting from a conversion of cot(pi/5). It discloses a bug in the max_to_sr method that I think we need to fix.

EmmanuelCharpentier commented 7 months ago

resulting from a conversion of cot(pi/5).

Hmmm ?

sage: maxima_calculus.cot((pi/5)._maxima_())
cot(%pi/5)
sage: maxima_calculus.cot((pi/5)._maxima_())._sage_()
1/5*sqrt(10*sqrt(5) + 25)
sage: maxima_calculus.cot(pi/5)._sage_()
1/5*sqrt(10*sqrt(5) + 25)
DaveWitteMorris commented 7 months ago

Yes, that is the problem. The buggy code expects expr and SR(maxima(expr)) to have the same main operator, but maxima's expr (essentially the first line in your example) is a cotangent, whereas sage's version SR(maxima(expr)) (essentially the second line in your example) is a product.

You can see this in action by adding lines to print op_max, op, expr, and SR(maxima(expr)). Just before the RuntimeError in the original report, op_max is cotangent, op is multiplication, expr is the maxima version of cot(pi/5), and SR(maxima(expr)) is 1/5*sqrt(10*sqrt(5) + 25). That's how I diagnosed the problem.

nbruin commented 7 months ago

To fix this, I think we would need to either prepopulate the translation dictionary extensively (not just a bit) or find a reliable way to query the translation dictionary that is used by the SR constructor.

The translation table is present in another form: SR <-> maxima started out as a normal string-based interface and somewhere in the system is a dictionary system that is largely prepopulated. It would tell you that the function with the string representation "cot" is SR corresponds to whatever "cot" produces in maxima.

max_to_sr and sr_to_max try something different: they need a dictionary that actually match up objects. Rather than running through the whole dict for the strings-based translation and translate that (and get people to update it) it seemed better for promoting adoption to try and automatically extract these translations from the existing strings-based dict.

However: you can't just give an identifier to the stringbased translator and expect to get the appropriate translation: it may not even be grammatical to do this! For instance, x+1 is add_vararg(x,1) and passing add_vararg is not going to give a valid result (and, when used as if + were the right string rep, it would even work less and possibly lead to ungrammatical results. Hence the need to pass the (sub) expression in which the operator was found: at least that's supposedly grammatical.

But as you can see here, more eager evaluation on one side than the other makes this approach problematic. And as this example shows, it can be hard to predict when we're liable to be exposed to that.

The table is there somewhere in the system, though. You could just try and see if you can extract it and then change it (manually or automatically) to a symbols dictionary for the internal representations.

Note that the translations aren't fully transitioned to sr_to_max and max_to_sr. There are parts where string-based translation is still happily used ... I guess because no-one felt the need to change it (very little used? not time critical?). But it does mean we need to keep both.