Ask Your Question

How to wrap a sympy function as a sage function ?

asked 2020-07-10 12:21:22 -0500

Emmanuel Charpentier gravatar image

Inspired by a recent question :

importing a function from sympy allows apparently to use a sympy function in sage:

sage: import sympy
sage: from sympy import sympify, sin as ssin
sage: ssin(sympify(a+b))
sin(a + b)

But this is purely cosmetic : this result does not have the methods of a Sage symbolic expression:

sage: ssin(sympify(a+b)).trig_expand()
AttributeError                            Traceback (most recent call last)
<ipython-input-15-859c8e851702> in <module>()
----> 1 ssin(sympify(a+b)).trig_expand()

AttributeError: 'sin' object has no attribute 'trig_expand'
sage: ssin(sympify(a+b)).operator()
AttributeError                            Traceback (most recent call last)
<ipython-input-16-d52ecf6bce28> in <module>()
----> 1 ssin(sympify(a+b)).operator()

AttributeError: 'sin' object has no attribute 'operator'

In fact, this is a sympy object :

sage: type(ssin(sympify(a+b)))


sage: type(sin(a+b))
<class 'sage.symbolic.expression.Expression'>

So, my question is :how toi create a Sage function that :

  • translates all its argument to the relevant sympy types

  • calls somehow the sympy functin on these arguments

  • translates back the result to Sage ?

This is done for some Sage functions, implemented by Maxima or sympy, in the Sage library. Is it possible to make this "lightly" at run time in some Sage source code without having to recompile the Sage library ?

edit retag flag offensive close merge delete

2 answers

Sort by » oldest newest most voted

answered 2020-07-10 13:47:20 -0500

dsejas gravatar image

updated 2020-07-10 13:55:33 -0500

Hello, @Emmanuel Charpentier! I am not 100% sure if this is exactly what you are looking for, but there exist these methods called _sympy_() and _sage_(). The first one converts some Sage expression to Sympy, while the second converts Maxima/Sympy/Giac/etc. expressions to Sage.

For example, I can wrap your code inside a function like this:

def sympy_sin(a, b):
    # Convert to Sympy (this renders the sympify method unnecessary in this case)
    sa = a._sympy_()
    sb = b._sympy_()

    from sympy import sin as ssin
    res = ssin(sa + sb)
    # Convert back to Sage and return result
    return res._sage_()

Now, you could do something like the following:

var('a b')
ss = sympy_sin(a, b)

and you will get the correct answer, cos(b)*sin(a) + cos(a)*sin(b). To further verify the conversion step, we do


and we get the expected <class 'sage.symbolic.expression.Expression'>.

Of course, it is NOT necessary to wrap this into a function, like I did above; you can use these methods in any place on Sage.

I hope this helps!

edit flag offensive delete link more


I'm looking to use these wraopers precisely in cases where the sumpy finctins of interest have NO _sage_() method nor Sage equivalent... precisely to give Sage the related abilities. Look at the quiestion I pointed to for an example of what I mean to do...

Emmanuel Charpentier gravatar imageEmmanuel Charpentier ( 2020-07-10 17:01:17 -0500 )edit

This is what I have asked elsewhere. Apparently the sympy "integrate()" function has functionality that Sagemath hasn't implemented; even with 'algorithm = "sympy" ' . In addition, "from sympy import * " changes the syntax of Sagemath "integrate" including limit formatting.
I will try your wrapper as a way to access this functionality although it would be more consistent to go through "algorithm='sympy' " for integration; and have the limit formatting done automatically; the same for mathematica expressions.

rrogers gravatar imagerrogers ( 2020-07-11 07:10:51 -0500 )edit

answered 2020-07-11 05:55:55 -0500

mwageringel gravatar image

Here is an attempt:

def my_lerch_phi_evalf(self, z, s, a, parent=None, algorithm=None):
    if parent is None:
        return self(z, s, a)._sympy_().evalf()._sage_()
        digits = ceil(log(float(2), float(10)) * parent.prec())
        return self(z, s, a)._sympy_().evalf(n=digits)._sage_()

my_lerch_phi = function('my_lerch_phi', nargs=3,

Now we can convert our function to SymPy:

sage: _ = var('z,s,a')
sage: f = my_lerch_phi(z, s, a); f
my_lerch_phi(z, s, a)
sage: f._sympy_()
lerchphi(z, s, a)
sage: unicode_art(f)
Φ(z, s, a)

and evaluate it numerically:

sage: my_lerch_phi(1, 2, 3).n()
sage: my_lerch_phi(1, 2, 3).n(100)
sage: my_lerch_phi(11/10, 2, 3).n()
0.420359955902005 - 0.224963005774297*I

Note that the precision is a bit higher than needed because we rounded up, so some more adjustments are needed to convert to the parent of the correct precision. Moreover, function has several more optional arguments that may be useful.

For the conversion from SymPy to Sage, one could use this hack which is based on the current implementation of _sympysage_function. (It might be better if the SymPy interface made use of the symbol_table for the conversion to Sage, like the Mathematica interface does, which allows for customization.)

sage: f._sympy_()._sage_()
sage: sage.functions.all.lerchphi = my_lerch_phi  # this is a hack
sage: f._sympy_()._sage_()  # now this works
my_lerch_phi(z, s, a)

Now also this gives a correct error message:

sage: my_lerch_phi(z, s, a).n()
TypeError: cannot evaluate symbolic expression numerically
edit flag offensive delete link more


It might be useful if the SymPy interface attempted to do all of this automatically, i.e. whenever there is no corresponding Sage function when converting to Sage, the interface could create such a wrapper on the fly, so that integrate(..., algorithm='sympy') produces useful results instead of raising an error.

mwageringel gravatar imagemwageringel ( 2020-07-11 07:35:36 -0500 )edit

I was thinking more along the lines of symbolic functions, in order to benefit from the "know-how" accumulated in sympy's code... Numerical computation is but one of the goals.

Automatic import is tempting, but I see at least two possible snags :

  • Argument lists : this automatic conversion might only handle unambiguous mandatory argument lists ; optional and keyword arguments might be trickier to handle.

  • global namespace cluttering : such a conversion should be memorized in order to alleviate repeated evaluation. The onluy logical space for this is the global namespace. But I'm wary of accumulating garbage...

I have to think this over. Would you mind comment on this comment to let me know further t

Emmanuel Charpentier gravatar imageEmmanuel Charpentier ( 2020-07-11 13:37:12 -0500 )edit

The function factory returns a subtype of SymbolicFunction. It is possible to customize the symbolic evaluation as well:

def my_lerch_phi_eval(self, *args):
    import sympy
    f = sympy.lerchphi(*(a._sympy_() for a in args))
    g = sympy.expand_func(f)
    return None if f == g else g._sage_()

my_lerch_phi = function('my_lerch_phi', nargs=3,


sage: _ = var('z,s,a')
sage: my_lerch_phi(z, s, a) * my_lerch_phi(z, s, 1)
my_lerch_phi(z, s, a)*polylog(s, z)/z
mwageringel gravatar imagemwageringel ( 2020-07-13 13:38:55 -0500 )edit

However, this seems to be much more difficult to implement generically, for arbitrary SymPy functions. In SymPy, expand_func is not applied automatically, so Sage should probably not do this either, but the functionality of expand_func does not seem to be accessible from within Sage.

Without defining eval_func, but just evalf_func, I think that such functions could be created on the fly. They could be cached, but do not need to be added to the global namespace, as they do not have any symbolic capabilities. Basically, it would just make integration return correct results that can also be evaluated numerically.

mwageringel gravatar imagemwageringel ( 2020-07-13 13:39:11 -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: 2020-07-10 12:21:22 -0500

Seen: 94 times

Last updated: Jul 11