Ask Your Question

Confusion about integrating a string.

asked 2019-12-26 17:44:03 -0500

Nasser gravatar image

updated 2019-12-26 20:16:53 -0500

sagemath 8.9

Follow up based on answer here why-sagemath-can-integrate-a-string sagemath can integrate a string.

Well. Sometimes it can, and sometimes it does not. And this getting confusing. For example

sage: integrate("ln(t)",t,algorithm="fricas")
integral(ln(t), t)

But if it is not a string, it works.

sage: integrate(ln(t),t,algorithm="fricas")
t*log(t) - t

But using "log(t)" as string, instead of "ln(t)" now it works

sage: integrate("log(t)",t,algorithm="fricas")
t*log(t) - t

Adding SR makes no difference

sage: integrate(SR("ln(t)"),t,algorithm="fricas")
integral(ln(t), t)
sage: integrate(SR("log(t)"),t,algorithm="fricas")
t*log(t) - t

Why does it fail when doing "ln(t)" but it works with "log(t)" ? I am sure there is a good reason.

And sometimes it crashes also

sage: integrate("ln(1-t)",t,algorithm="fricas")
TypeError                                 Traceback (most recent call last)
<ipython-input-24-c1ae517e9ade> in <module>()
----> 1 integrate("ln(1-t)",t,algorithm="fricas")

/usr/lib/python2.7/site-packages/sage/misc/functional.pyc in integral(x, *args, **kwds)
    754     else:
    755         from sage.symbolic.ring import SR
--> 756         return SR(x).integral(*args, **kwds)

/usr/lib/python2.7/site-packages/sage/symbolic/expression.pyx in sage.symbolic.expression.Expression.integral (build/cythonized/sage/symbolic/expression.cpp:64032)()
  12360                     R = ring.SR
  12361             return R(integral(f, v, a, b, **kwds))
> 12362         return integral(self, *args, **kwds)
  12364     integrate = integral

/usr/lib/python2.7/site-packages/sage/symbolic/integration/integral.pyc in integrate(expression, v, a, b, algorithm, hold)
    910         if not integrator:
    911             raise ValueError("Unknown algorithm: %s" % algorithm)
--> 912         return integrator(expression, v, a, b)
    913     if a is None:
    914         return indefinite_integral(expression, v, hold=hold)

/usr/lib/python2.7/site-packages/sage/symbolic/integration/external.pyc in fricas_integrator(expression, v, a, b, noPole)
    382     from sage.interfaces.fricas import fricas
--> 383     ex = fricas(expression)
    385     if a is None:

/usr/lib/python2.7/site-packages/sage/interfaces/interface.pyc in __call__(self, x, name)
    293             # user-assigned name might change its value, so we return a
    294             # new element.
--> 295             result = self._coerce_from_special_method(x)
    296             return result if name is None else
    297         except TypeError:

/usr/lib/python2.7/site-packages/sage/interfaces/interface.pyc in _coerce_from_special_method(self, x)
    321             s = '_gp_'
    322         try:
--> 323             return (x.__getattribute__(s))(self)
    324         except AttributeError:
    325             return self(x._interface_init_())

/usr/lib/python2.7/site-packages/sage/structure/sage_object.pyx in sage.structure.sage_object.SageObject._fricas_ (build/cythonized/sage/structure/sage_object.c:7272)()
    744             import sage.interfaces.fricas
    745             G = sage.interfaces.fricas.fricas
--> 746         return self._interface_(G)
    748     def _fricas_init_(self):

/usr/lib/python2.7/site-packages/sage/symbolic/expression.pyx in sage.symbolic.expression.Expression._interface_ (build/cythonized/sage/symbolic/expression.cpp:7633)()
    797         if is_a_constant(self._gobj):
    798             return self.pyobject()._interface_(I)
--> 799         return super(Expression, self)._interface_(I)
    801     def _maxima_(self, session=None):

/usr/lib/python2.7/site-packages/sage/structure/sage_object.pyx in sage.structure.sage_object.SageObject._interface_ (build/cythonized/sage/structure/sage_object.c:5474)()
    667             except Exception:
    668                 raise NotImplementedError("coercion of object %s to %s not implemented:\n%s\n%s" % (repr(self), I))
--> 669         X = I(s)
    670         if c:
    671             try:

/usr/lib/python2.7/site-packages/sage/interfaces/interface.pyc in __call__(self, x, name)
    287         if isinstance(x, string_types):
--> 288             return cls(self, x, name=name)
    289         try:
    290             # Special methods do not and should not have an option to

/usr/lib/python2.7/site-packages/sage/interfaces/expect.pyc in __init__(self, parent, value, is_name, name)
   1474             except (RuntimeError, ValueError) as x:
   1475                 self._session_number = -1
-> 1476                 raise_(TypeError, TypeError(*x.args), sys.exc_info()[2])
   1477             except BaseException:
   1478                 self._session_number = -1

/usr/lib/python2.7/site-packages/sage/interfaces/expect.pyc in __init__(self, parent, value, is_name, name)
   1469         else:
   1470             try:
-> 1471                 self._name = parent._create(value, name=name)
   1472             # Convert ValueError and RuntimeError to TypeError for
   1473             # coercion to work properly.

/usr/lib/python2.7/site-packages/sage/interfaces/interface.pyc in _create(self, value, name)
    489     def _create(self, value, name=None):
    490         name = self._next_var_name() if name is None else name
--> 491         self.set(name, value)
    492         return name

/usr/lib/python2.7/site-packages/sage/interfaces/fricas.pyc in set(self, var, value)
    588         cmd = '%s%s%s;' % (var, self._assign_symbol(), value)
    589         output = self.eval(cmd, reformat=False)
--> 590         self._check_errors(value, output)
    592     def get(self, var):

/usr/lib/python2.7/site-packages/sage/interfaces/fricas.pyc in _check_errors(self, line, output)
    562             for old, new in replacements:
    563                 output = output.replace(old, new)
--> 564             raise RuntimeError("An error occurred when FriCAS evaluated '%s':\n%s" % (line, output))
    566         # or even an error

TypeError: An error occurred when FriCAS evaluated 'operator("ln")(((t)*(-1))+(1))':
   There are 1 exposed and 1 unexposed library operations named elt having
      1 argument(s) but none was determined to be applicable. Use HyperDoc
      Browse, or issue
                                )display op elt
      to learn more about the available operations. Perhaps 
      package-calling the operation or using coercions on the arguments 
      will allow you to apply the operation.

   Cannot find application of object of type BasicOperator to argument(s) 
      of type(s) 

But OK if not a string

sage: integrate(ln(1-t),t,algorithm="fricas")
(t - 1)*log(-t + 1) - t

ps. The whole idea of allowing a string as input to integrate is not right IMHO, but I assume it is allowed for a good reason. If it is to be allowed, it should be treated as a constant with no interpretation at all. This is what Mathematica does:

image description

But I think Maple handles this best by rejecting a string all together

Error, (in int) wrong number (or type) of arguments: wrong type of integrand passed to indefinite integration.

Another observation: This seems to affect fricas and maxima, but not giac

sage: integrate("ln(t)",t,algorithm="fricas")
integral(ln(t), t)
sage: integrate("ln(t)",t,algorithm="giac")
t*log(t) - t
sage: integrate("ln(t)",t,algorithm="maxima")
integrate(ln(t), t)

Thank you --Nasser

edit retag flag offensive close merge delete

2 answers

Sort by ยป oldest newest most voted

answered 2019-12-26 20:29:08 -0500

dsejas gravatar image

updated 2019-12-27 11:12:36 -0500

Hello, @Nasser! This seems to be Fricas' fault, since it implements the log function, but not the ln function. However, Sage implements both; although the preferred name is log.

When you call integrate("log(t)", t, algorithm="fricas"), the following steps are executed:

ex = "log(t)"
if not isinstance(ex, Expression):
    ex = SR(ex) # here ex becomes log(t), an Expression, not a string
ex= fricas(ex) # ex is translated to fricas representation
result = ex.integrate(fricas(t)) # we call the fricas' integrate function with the variable t converted to fricas

If you could print result at this stage, you would get

t log(t) - t

which is finally converted to Sage representation with the command


This shows you

t*log(t) - t

Notice the subtle difference between the Fricas representation and the Sage representation?

When you try to integrate an unknown function in Fricas, lets say func, Fricas returns the not satisfactory, although completely correct answer

 |   func(%A)d%A

(This is what you get from the step result = ex.integrate(fricas(t)) that I mentioned before.) The problem here is that Fricas doesn't know how to integrate func, so it returns an abstract representation of the integral. After translating that to Sage's representation (with the step result.sage() mentioned above), it is shown as

integral(func(t), t)

which is the equivalent Sage representation of the abstract integral, and is also correct, but not satisfactory.

EDIT Based on the answer to this question, I must point out that the use of strings as functions, by means of the Symbolic Ring SR, is discourage and should be avoided, because it has unpleasant consequences like this one.. This kind of use of strings seems not to be documented for that reason.

I hope this helps!

edit flag offensive delete link more


This explanation is similar to the answer I gave to this question you asked. You can compare both processes of working with Giac and Fricas.

If you really have problems using external software to integrate, maybe you can leave the algorithm parameter out, and let Sage do its magic.

dsejas gravatar imagedsejas ( 2019-12-26 20:34:15 -0500 )edit

"since it implements the log function, but not the ln function" But I am using sagemath? If a user has to know what each other CAS system that sagemath uses prefer or not prefer in terms of the input, what is the point then of using Sagemath? One can just use the other CAS system. I mean, I am using ln(t) which Sagemath knows, right? A user should not care if the system that sagemath ends up calling to do the integration knows or not know about ln(t). Sagemath should have then converted ln(t) automatically internally to log(t) in this case. At least this is what I would have expected. But thanks for the answer. I changed my calls to use "log(t)" and issue resolved for me.

Nasser gravatar imageNasser ( 2019-12-26 21:55:28 -0500 )edit

Hello, @Nasser! You make an excellent point. I have to acknowledge that I was wrong, and you are right: SageMath should be able to deal with the ln-to-log conversion when using Fricas. So, we should consider this a bug. I have edited my question to reflect this fact, and I have posted a question for bug confirmation.

dsejas gravatar imagedsejas ( 2019-12-27 08:47:35 -0500 )edit

Hello, @Nasser! Check my new edit to my answer. After reporting this as a possible bug, it seems that this kind of use of strings in integration is discourage, exactly for reasons like this. The fact that Sage "can" indeed integrate strings is an consequence of the use of SR during integration, but is not a documented procedure, nor a correct use of Sage. However, consequently, I think there should be a warning when trying to pass a string as integrand.

dsejas gravatar imagedsejas ( 2019-12-27 11:18:12 -0500 )edit

answered 2019-12-27 10:53:51 -0500

Emmanuel Charpentier gravatar image

Don't do that, for reasons explained in my answer to your followup question.

Sage functions and methods are documented. Your use case is NOT documented, and depends on an undocumented implementation detail. This way lies madness...

edit flag offensive delete link more


I generally agree with this: If you engage in undefined behavior (passing a string as the argument to integrate() then undefined (and hence unpredictable) behavior is what you get. And because Python does not perform type checking, you could pass literally any object to integrate() and maybe it will happen to work in some corner cases; most of the time it will crash. It is not standard practice in Python to manually strictly type-check all function inputs, especially given that it's possible one could, e.g., define a str subclass that duck-types as whatever type(s) the function is expecting. Not saying there's always necessarily a good reason to do that, but sometimes there is! Python trades strictness for this kind of flexibility and "consenting adult" approach to coding.

Iguananaut gravatar imageIguananaut ( 2019-12-31 04:23:17 -0500 )edit

OTOH I think str (and most other common built-in types in Python) do deserve special consideration given their commonality, and the fact that they have a literal syntax built into the language. And I don't think it's entirely unreasonable that one might pass as symbolic expression as a string to functions like integrate() and derivative() that explicitly operate on symbolic expressions. So I think @Nasser is well-motivated to suggest that str should be given special consideration here, and that it does appear a bit sloppy that it works in many cases but not all--it's not completely random as it might be with some more arbitrary inputs, since e.g. fricas will accept some strings, but not all. I think this deserves more careful consideration.

Iguananaut gravatar imageIguananaut ( 2019-12-31 04:27:10 -0500 )edit

IMO the string should be parsed into a generic symbolic expression and converted into some backend-agnostic representation, but I'm still not 100% sure how symbolic expressions really work in Sage and if something like that is exactly possible...

Iguananaut gravatar imageIguananaut ( 2019-12-31 04:29:34 -0500 )edit

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Question Tools

1 follower


Asked: 2019-12-26 17:44:03 -0500

Seen: 65 times

Last updated: Dec 27 '19