# Symbolic functions without named variables

Is there a way to define a symbolic function that can (e.g.) be differentiated, but doesn't remember the name of its input variable(s)? For instance, consider:

sage: f(x) = x^2
sage: g(x) = x^2
sage: h(t) = t^2


Mathematically, f, g, and h, should all be the same function. However, Sage doesn't think so:

sage: f+g
x |--> 2*x^2
sage: f+h
(t, x) |--> t^2 + x^2


I guess that this is happening because a "function" defined with f(x)=x^2 is actually just a symbolic expression equipped with an ordering on its variables, rather than what a mathematician would call a "function". Is there a way to define an actual mathematical function?

edit retag close merge delete

Sort by » oldest newest most voted

Depending on what you want, the underlying tree for a symbolic expression might also be useful. A naive function that takes a symbolic expression as input and returns an expression tree is demonstrated here.

Sage has more sophisticated ways of building expression trees for the purposes of creating fast callable functions (for, e.g., plotting graphs or computing numerical integrals). I find them a bit confusing, but they might prove useful. The relevant classes are Expression and ExpressionTreeBuilder, both defined in sage.ext.fast_callable

Here is a demonstration of how they might be used, and also a "variable free" version of the naive tree:

from sage.ext.fast_callable import ExpressionTreeBuilder

class AbstractFunction(SageObject):
"""
A class to compute and store information for abstract (variable-free) functions
"""
def __init__(self,f):
self._vars = f.variables()

# an ExpressionTreeBuilder instance must know
# the names and ordering of variables
self.etb = ExpressionTreeBuilder(vars=self._vars)

# store the symbolic expression, since we don't
# know how to rebuild it
self.symbolic_expr = f

# the Expression object
self.expr = self.etb(f)

# the fast callable version of this Expression
# useful for computing values of the function
self.callable_expr = fast_callable(self.expr)

# a naive expression tree, stored as a list of lists
self.naive_tree = self.extract_naive_tree(f)

def vars(self,i=None):
if i is not None:
return lambda V: V[i]
return self._vars
def reset_vars(self,new_vars):
"""
Change the variable names for the underlying symbolic expression
"""
sub_dict = dict(zip(self._vars,new_vars))
self.symbolic_expr = self.symbolic_expr.subs(sub_dict)
self._vars = new_vars
return self.symbolic_expr

def extract_naive_tree(self,expr):
"""
A nested list of operators and operands.
Variables are replaced by coordinate functions which
extract ith value from list of inputs.
"""
if expr.operator() is None:
try:
v0 = expr.variables()[0] # test whether we have non-trivial tuple of variables
return [self.vars,self._vars.index(expr)]
except IndexError:
return expr
else:
return [expr.operator()]+map(self.extract_naive_tree,expr.operands())

def diff(self,*args,**kwds):
"""
Differentiate the symbolic expression and
convert the result to an AbstractFunction
"""
df = self.symbolic_expr.diff(*args,**kwds)
return AbstractFunction(df)


Even though this is getting too long, here are some examples of using this class:

sage: var('x,y,z')
(x, y, z)


Create AbstractFunction from symbolic expression

sage: f = AbstractFunction(x^2 + 1)
sage: f.symbolic_expr # original symbolic expression
x^2 + 1
sage: f.expr # Expression object
sage: f.naive_tree # naive expression tree
[<function operator.pow>,
[<bound method AbstractFunction.vars of <class '__main__.AbstractFunction'>>,
0],
2],
1]
sage: f.callable_expr(3) # calling the callable version of the expression
10
sage: f.callable_expr(z+x) # calling with symbolic expressions as inputs
(x + z)^2 + 1


Operating on the symbolic expression and converting the result to an AbstractFunction:

sage: df = f.diff()
sage: df.symbolic_expr
2*x
sage: df.expr
mul(v_0, 2)


Change variable names for the original symbolic expression:

sage: f.reset_vars((z,))
z^2 + 1
sage: f.diff().symbolic_expr
2*z


A multivariable example:

sage: h ...
more

I'm trying to understand this. Is the expression tree really necessary? It seems like a wrapper around a callable symbolic expression that knows how to reset the variables as needed might be sufficient for what I want. For instance, could you also overload function application, addition, etc.?

( 2013-12-02 16:49:58 -0600 )edit

Sorry for the complexity -- I was learning as I wrote this, and I think it contains at least two independent solutions. But I'm also not sure what you want. A wrapper which changes variables in a symbolic expression seems reasonable too. The expression tree gives a presentation of a symbolic expression as a composite of elementary functions -- that may or may not be useful depending on how you want to work with abstract functions.

( 2013-12-03 00:42:31 -0600 )edit

It's a peculiarity of sage that object "(print)names" have significance. For instance, QQ['x'] and QQ['t'] are both rings of univariate polynomials over QQ. They are of course isomorphic but in the eyes of sage not canonically so (there are many relations possible between x and t that could establish an isomorphism), and hence sage declines to choose any by default. The same here:

sage: parent(f)
Callable function ring with arguments (x,)
sage: parent(h)
Callable function ring with arguments (t,)


Going from one to the other can be done:

sage: Ct = parent(h)
sage: ft = Ct(f(t))
sage: ft
t |--> t^2
sage: parent(ft)
Callable function ring with arguments (t,)
sage: ft+h
t |--> 2*t^2
sage: parent(ft+h)
Callable function ring with arguments (t,)


Doing it like this might give you a bit of a glimpse "under the hood" into why it works this way.

more

Hmm... if it were really consistent about behaving this way, then I would expect f+h to be a type error, since you can't add elements of different rings.

( 2013-12-02 16:34:28 -0600 )edit

You can if you consider the two rings as subrings of a bigger ring, and in this case there is an (almost) natural candidate: the Callable function ring with arguments (x,t), since a function in either x or t can be considered as a function in both. The fact that the order of the arguments isn't well-defined is problematic, though. Finding a common "covering structure" in which the result of an operation on two elements from distinct structures lives is referred to as a "pushout" in sage.

( 2013-12-04 03:19:01 -0600 )edit

Okay, I guess I see what the developers were thinking, even if I don't agree with it. (Wouldn't "coproduct" be a more appropriate term?)

( 2013-12-05 06:29:59 -0600 )edit

You are right, you defined a symbolic expression, which includes the name of the variable:

sage: type(f)
<type 'sage.symbolic.expression.Expression'>

sage: f.parent()
Callable function ring with arguments (x,)


You can survive by noticing that:

sage: i(t) = f(t) + g(t)
sage: i
t |--> 2*t^2


Unfortunately, it is currently not possible to add more semantics to such objects, therefore approaching mathematical functions. Typically, it would be very nice to be able to define a function, including its domain and codomain (this is currently imitated with assumptions on the variables).

more

This has a side-effect (the string returned by preparse is what is actually executed): sage: preparse("i(t)=f(t)+g(t)") '__tmp__=var("t"); i = symbolic_expression(f(t)+g(t)).function(t)' In addition to defining i as the required symbolic function, the symbol t also gets re-defined as a symbolic variable. That's a side-effect you may not always expect: sage: t=1 sage: f(t)=t+1 sage: f t |--> t + 1 sage: t # t has been redefined too t

( 2013-11-28 06:56:56 -0600 )edit

Thanks! Why do you refer to defining mathematical functions as "adding more semantics"?

( 2013-12-02 16:31:29 -0600 )edit