How to create a symbolic function returning a function ?
[ 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 necessaryaccept 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 ?
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.