sagemath / sage

Main repository of SageMath
https://www.sagemath.org
Other
1.31k stars 450 forks source link

sagemath 9.8-Fricas interface. RecursionError: on integral which works ok inside Fricas #35241

Open nasser1 opened 1 year ago

nasser1 commented 1 year ago

Is there an existing issue for this?

Did you read the documentation and troubleshoot guide?

Environment

- **OS**: 6.1.12-1-MANJARO
- **Sage Version**:  9.8

sagemath gives RecursionError  when calling integrate with fricas as algorithm, but these integrals work with no problem inside fricas directly. I am using latest Fricas build from sources on Linux

>fricas --version
FriCAS 1.3.8
based on ecl 21.2.1

To reproduce:

>sage
┌────────────────────────────────────────────────────────────────────┐
│ SageMath version 9.8, Release Date: 2023-02-11                     │
│ Using Python 3.11.1. Type "help()" for help.                       │
└────────────────────────────────────────────────────────────────────┘
sage: var('x')
sage: integrate(x*(1-x)^2014,x,algorithm="fricas")
....
  1310     a += 1
   1312 if s[a] == FriCASElement._LEFTBRACKET:
-> 1313     return FriCASElement._parse_list(s, start=a)
   1314 elif s[a] == FriCASElement._STRINGMARKER:
   1315     return FriCASElement._parse_string(s, start=a)

RecursionError: maximum recursion depth exceeded

Second example

integrate(x*(1-x)^2020,x,algorithm="fricas")

Gives same error.



### Steps To Reproduce

_No response_

### Expected Behavior

Expected to return same result for the integration as Fricas gives directly. i.e. no error. 

### Actual Behavior

Exception is thrown.

### Additional Information

_No response_
mantepse commented 1 year ago

There is no need to invoke integration:

sage: p = x*(1-x)^2014
sage: q = fricas(p)
sage: r = q.sage()

also fails. Note, however, that p is expanded (fricas always expands powers), which yields a rather large expression.

The current parser (lines 1269-1480 of fricas.py works recursively, which is bound to fail on such expressions. I am not sure whether it pays off to rewrite it.

mantepse commented 1 year ago

There is one optimization, which won't help for this ticket, but might be worthwhile anyway:

iff --git a/src/sage/interfaces/fricas.py b/src/sage/interfaces/fricas.py
index 202a756b0e..403ce1dc9e 100644
--- a/src/sage/interfaces/fricas.py
+++ b/src/sage/interfaces/fricas.py
@@ -1988,6 +1988,28 @@ class FriCASElement(ExpectElement, sage.interfaces.abc.FriCASElement):
                       for i in range(ZZ(self.degree()) + 1)])

         # finally translate domains with InputForm
+        if head in ["OrderedCompletion", "OnePointCompletion", "Expression", "Pi"]:
+            # it would be more correct to get the type parameter
+            # (which might not be Expression Integer) and recurse
+            # we treat Expression Integer and Expression Complex
+            # Integer just the same
+            return FriCASElement._sage_expression(P.get_InputForm(self._name))
+
+        if head == "Polynomial":
+            base_ring = self._get_sage_type(domain[1])
+            # Polynomial Complex is translated into SR
+            if base_ring is SR:
+                return FriCASElement._sage_expression(P.get_InputForm(self._name))
+
+            # the following is a bad hack, we should be getting a list here
+            vars = P.get_unparsed_InputForm("variables(%s)" % self._name)[1:-1]
+            unparsed_InputForm = P.get_unparsed_InputForm(self._name)
+            if vars == "":
+                return base_ring(unparsed_InputForm)
+
+            R = PolynomialRing(base_ring, vars)
+            return R(unparsed_InputForm)
+
         try:
             unparsed_InputForm = P.get_unparsed_InputForm(self._name)
         except RuntimeError as error:
@@ -2024,30 +2046,6 @@ class FriCASElement(ExpectElement, sage.interfaces.abc.FriCASElement):
             n = n[:n.find(")")]
             return self._get_sage_type(domain)(n)

-        if head == "Polynomial":
-            base_ring = self._get_sage_type(domain[1])
-            # Polynomial Complex is translated into SR
-            if base_ring is SR:
-                return FriCASElement._sage_expression(P.get_InputForm(self._name))
-
-            # the following is a bad hack, we should be getting a list here
-            vars = P.get_unparsed_InputForm("variables(%s)" % self._name)[1:-1]
-            if vars == "":
-                return base_ring(unparsed_InputForm)
-            else:
-                R = PolynomialRing(base_ring, vars)
-                return R(unparsed_InputForm)
-
-        if head in ["OrderedCompletion", "OnePointCompletion"]:
-            # it would be more correct to get the type parameter
-            # (which might not be Expression Integer) and recurse
-            return FriCASElement._sage_expression(P.get_InputForm(self._name))
-
         if head == 'DistributedMultivariatePolynomial':
             base_ring = self._get_sage_type(domain[2])
             vars = domain[1].car()