How to create a symbolic function returning a function ?

asked 2019-12-11 22:01:31 +0200

Emmanuel Charpentier gravatar image

updated 2022-10-27 20:17:09 +0200

FrédéricC gravatar image

[ Context: implementing a root_sum function analogous to Sympy's RootSum and Maxima's similar function, whose name currently escapes me... A similar functionality exist also in Fricas, and can be met within its results.. ]

Sympy has a function Lambda, that creates a Sympy Function from a (tuple of) formal argumen(s) and a (Sympy) expression. I want to create a similar function in Sage. Such a function should:

  • accept a hold parameter (and therefore stay unevaluatd if necessary

  • accept two-ways conversions (in Sympy at first, but similar cases can be made for Mathematica and Fricas)

  • return either a lambda Python anonymous function or a Sage symbolic function.

Creating such a function is relatively easy : function (or its related sage.symbolic.function_factory.function) can almost do the necessary setup:

# Eval the rgument and return the correct symbolic function:
def eval_symbolic_lambda(self, args, body):
    from sympy.core.compatibility import iterable
    if not iterable(args): args=[args]
    sargs=tuple([u._sage_() for u in args])
    vargs=tuple([u._sage_() for u in body._sage_().variables()])
    vargs=tuple(set(vargs)-set(vargs))
    for w in sargs:
        exec("{}=SR.var('{}')".format(repr(w),repr(w)))
    for w in vargs:
        exec("{}=SR.var('{}')".format(repr(w),repr(w)))
    return eval(preparse(repr(body._sage_()))).function(*sargs)

# The symbolic functuin itself
symbolic_lambda=sage.symbolic.function_factory.function("symbolic_lambda",
                                                                                         nargs=2,
                                                                                         conversions=dict(sympy="Lambda"),
                                                                                         eval_func=eval_symbolic_lambda)

The evaluation function does its job of building the relevant symbolic function :

sage: foo=eval_symbolic_lambda(None, x,x^2+1);foo
x |--> x^2 + 1
sage: foo(3)
10
sage: foo.parent()
Callable function ring with argument x
sage: type(foo)
<class 'sage.symbolic.expression.Expression'>

The symbolic function can be held:

sage: gee=symbolic_lambda(x, x^2-1, hold=True); gee
symbolic_lambda(x, x^2 - 1)

It can also (correctly) be translated to Sympy:

sage: gee._sympy_()
Lambda(x, x**2 - 1)
sage: type(gee._sympy_())
<class 'sympy.core.function.Lambda'>

But trying to use it with evaluation fails: the result of eval_symbolic_lambda, which is a symbolic function, cannot be coerced to a symbolic expression :

sage: symbolic_lambda(x, x^2-1)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/usr/local/sage-P3-2/local/lib/python3.7/site-packages/sage/libs/pynac/pynac.pyx in sage.libs.pynac.pynac.pyExpression_to_ex (build/cythonized/sage/libs/pynac/pynac.cpp:5373)()
    182     try:
--> 183         t = ring.SR.coerce(res)
    184     except TypeError as err:

/usr/local/sage-P3-2/local/lib/python3.7/site-packages/sage/structure/parent.pyx in sage.structure.parent.Parent.coerce (build/cythonized/sage/structure/parent.c:10562)()
   1113 
-> 1114     cpdef coerce(self, x):
   1115         """

/usr/local/sage-P3-2/local/lib/python3.7/site-packages/sage/structure/parent.pyx in sage.structure.parent.Parent.coerce (build/cythonized/sage/structure/parent.c:10491)()
   1143                     _record_exception()
-> 1144             raise TypeError(_LazyString(_lazy_format, ("no canonical coercion from %s to %s", parent(x), self), {}))
   1145         else:

TypeError: no canonical coercion from Callable function ring with argument x to Symbolic Ring

During handling of the above exception, another exception occurred:

TypeError                                 Traceback (most recent call last)
<ipython-input-73-c250078eff24> in <module>()
----> 1 symbolic_lambda(x, x**Integer(2)-Integer(1))

/usr/local/sage-P3-2/local/lib/python3.7/site-packages/sage/symbolic/function.pyx in sage.symbolic.function.Function.__call__ (build/cythonized/sage/symbolic/function.cpp:7039)()
    600                     (<Expression>args[0])._gobj, hold)
    601         elif self._nargs == 2:
--> 602             res = g_function_eval2(self._serial, (<Expression>args[0])._gobj,
    603                     (<Expression>args[1])._gobj, hold)
    604         elif self._nargs == 3:

/usr/local/sage-P3-2/local/lib/python3.7/site-packages/sage/libs/pynac/pynac.pyx in sage.libs.pynac.pynac.pyExpression_to_ex (build/cythonized/sage/libs/pynac/pynac.cpp:5425)()
    183         t = ring.SR.coerce(res)
    184     except TypeError as err:
--> 185         raise TypeError("function did not return a symbolic expression or an element that can be coerced into a symbolic expression")
    186     return (<Expression>t)._gobj
    187 

TypeError: function did not return a symbolic expression or an element that can be coerced into a symbolic expression

Furthermore, at least in this use in "interactive mode", the mechanism allowing this function to be used for retro-converting a Sympy object to sage doesn't work:

sage: bar=sympy.Lambda(*[sympy.sympify(u) for u in (x, x^2-1)]);bar
Lambda(x, x**2 - 1)
sage: bar._sage_()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-102-5b91e5ff707e> in <module>()
----> 1 bar._sage_()

AttributeError: 'Lambda' object has no attribute '_sage_'

Any suggestion to solve those two problems ?

edit retag flag offensive close merge delete

Comments

It is from here where i stay hard to understand the question, since it is a hard time to see all what works, and finally it is not simple to see what should work, but it is not working. The question depends on (pep8-non-conformal) code, it is hard to read it, digesting it should then follow. The title may be also misleading. So i decided to ask my own questions... What do we need exactly (from the start, straight to the point)? The code should work in sage? Why do we need sympy? Why do we use Lambda expressions and not simple lambda expressions or best functions inside a function? Final note: If we need such a complicated avatar, maybe we rather need a better class / organization of data.

dan_fulea gravatar imagedan_fulea ( 2019-12-13 12:02:38 +0200 )edit