Ticket #12801 (closed defect: fixed)
substitute_function misses some substitutions
| Reported by: | nbruin | Owned by: | burcin |
|---|---|---|---|
| Priority: | minor | Milestone: | sage-5.0 |
| Component: | symbolics | Keywords: | |
| Cc: | mjo | Work issues: | |
| Report Upstream: | N/A | Reviewers: | Michael Orlitzky |
| Authors: | Nils Bruin | Merged in: | sage-5.0.beta14 |
| Dependencies: | #12796 | Stopgaps: |
Description (last modified by nbruin) (diff)
Works correctly:
sage: f=function('f')
sage: g=function('g')
sage: (f(g(x))+g(x)).substitute_function(g,sin)
sin(x) + f(sin(x))
but
sage: f(g(x)).diff(x).substitute_function(g,sin) cos(x)*D[0](f)(g(x)) sage: sage.symbolic.operators.FDerivativeOperator(f,[0])(g(x)).substitute_function(g,sin) D[0](f)(g(x))
so I suspect it forgets to descend into the arguments of an FDerivativeOperator
Attachments
Change History
comment:1 Changed 14 months ago by nbruin
- Status changed from new to needs_review
OK, that was easy.
comment:2 Changed 14 months ago by mjo
Even with the patch I don't think the second case is fixed completely. I was going to add a doctest to Expression.substitute_function(), but,
sage: f = function('f')
sage: g = function('g')
sage: f(g(x)).diff(x).substitute_function(g, sin)
ERROR: An unexpected error occurred while tokenizing input
The following traceback may be corrupted or invalid
The error message is: ('EOF in multi-line statement', (38, 0))
---------------------------------------------------------------------------
NotImplementedError Traceback (most recent call last)
/home/mjo/src/sage-5.0.beta11/devel/sage-main/<ipython console> in <module>()
/home/mjo/src/sage-5.0.beta11/local/lib/python2.7/site-packages/sage/symbolic/expression.so in sage.symbolic.expression.Expression.substitute_function (sage/symbolic/expression.cpp:16628)()
/home/mjo/src/sage-5.0.beta11/local/lib/python2.7/site-packages/sage/symbolic/expression_conversions.pyc in __call__(self, ex)
212 div = self.get_fake_div(ex)
213 return self.arithmetic(div, div.operator())
--> 214 return self.arithmetic(ex, operator)
215 elif operator in relation_operators:
216 return self.relation(ex, operator)
/home/mjo/src/sage-5.0.beta11/local/lib/python2.7/site-packages/sage/symbolic/expression_conversions.pyc in arithmetic(self, ex, operator)
1572 x*bar(x) + pi/bar(x)
1573 """
-> 1574 return reduce(operator, map(self, ex.operands()))
1575
1576 def composition(self, ex, operator):
/home/mjo/src/sage-5.0.beta11/local/lib/python2.7/site-packages/sage/symbolic/expression_conversions.pyc in __call__(self, ex)
216 return self.relation(ex, operator)
217 elif isinstance(operator, FDerivativeOperator):
--> 218 return self.derivative(ex, operator)
219 else:
220 return self.composition(ex, operator)
/home/mjo/src/sage-5.0.beta11/local/lib/python2.7/site-packages/sage/symbolic/expression_conversions.pyc in derivative(self, ex, operator)
1611 """
1612 if operator.function() == self.original:
1613 return operator.change_function(self.new)(*map(self,ex.operands()))
1614 else:
-> 1615 return operator(*map(self, ex.operands()))
/home/mjo/src/sage-5.0.beta11/local/lib/python2.7/site-packages/sage/symbolic/operators.pyc in __call__(self, *args)
42 if (not all(is_SymbolicVariable(x) for x in args) or
43 len(args) != len(set(args))):
---> 44 raise NotImplementedError, "currently all arguments must be distinct variables"
45 vars = [args[i] for i in self._parameter_set]
46 return self._f(*args).diff(*vars)
NotImplementedError: currently all arguments must be distinct variables
comment:4 Changed 14 months ago by nbruin
If you think this is an essential doctest, then you can make this dependent on #12796.
comment:5 follow-up: ↓ 6 Changed 14 months ago by mjo
- Dependencies set to #12796
With #12796 done, the example works and I can add a doctest. My own code needs to substitute functions that aren't built in, though:
sage: f = function('f')
sage: g = 2*x*sin(x)
sage: f_prime = f(x).diff(x)
sage: f_prime.substitute_function(f,g)
/home/mjo/src/sage-5.0.beta12/local/lib/python2.7/site-packages/sage/symbolic/expression_conversions.py:1627: DeprecationWarning: Substitution using function-call syntax and unnamed arguments is deprecated and will be removed from a future release of Sage; you can use named arguments instead, like EXPR(x=..., y=...)
return operator.change_function(self.new)(*map(self,ex.operands()))
2*x*cos(x) + 2*sin(x)
It does do the right thing, I just haven't been able to figure out how to avoid the warning. Is there some way to get a real function out of the expression g? Or a way to trick the substitution into doing e.g. x=x, y=y?
comment:6 in reply to: ↑ 5 Changed 14 months ago by nbruin
Replying to mjo:
Is there some way to get a real function out of the expression g?
Yes there is:
sage: f = function('f')
sage: gexpr = 2*x*sin(x)
sage: g = gexpr.function(x)
sage: g
x |--> 2*x*sin(x)
evil shorthand (do preparse('g(x)=2*x*sin(x)') or google sage-devel to see what's evil about it):
sage: g(x)=2*x*sin(x) sage: g x |--> 2*x*sin(x)
but then it works:
sage: f_prime = f(x).diff(x) sage: f_prime 2*x*cos(x) + 2*sin(x)
or, if you really want to stick with functions, things get a little hairier:
sage: f_prime=sage.symbolic.operators.FDerivativeOperator(f,[0]) sage: f_prime.substitute_function(f,g) AttributeError sage: f_prime.change_function(g) D[0](x |--> 2*x*sin(x)) sage: f_prime.change_function(g)(x) 2*x*cos(x) + 2*sin(x) sage: f_prime.change_function(g)(x).function(x) x |--> 2*x*cos(x) + 2*sin(x)
This basically shows you that arithmetic on functions themselves isn't really supported yet. Given that functions can have an unspecified arity that might be a good thing. If we want to make "symbolic functions" into a (differential) ring, some serious design choices would have to be considered.
In fact, that trick was used in #12796 in FDerivativeOperator.__call__. I don't know which of
expr.subs({x: x0, y: y0})
expr.function(x,y)(x0,y0)
is preferable.
comment:7 follow-up: ↓ 9 Changed 14 months ago by mjo
- Reviewers set to Michael Orlitzky
I'd like to look for a way to make it work for both substitute_function(g, sin) and substitute_function(g, 2*x), but there's no reason to hold this up. I can create a new ticket about the deprecation warning after these are merged.
You can make it positive review if the doctest is OK.
comment:8 Changed 14 months ago by nbruin
- Status changed from needs_review to positive_review
- Description modified (diff)
comment:9 in reply to: ↑ 7 Changed 14 months ago by nbruin
Replying to mjo:
I'd like to look for a way to make it work for both substitute_function(g, sin) and substitute_function(g, 2*x),
Unfortunately, there is not enough information to make this work reliably. If you do
expr=(c+1)*(y-1) sin(x).substitute_function(sin,expr)
it needs to turn (c+1)*(y-1) into a function. Any of the following are reasonable:
expr.function(y) expr.function(c) expr.function(y,c) expr.function(c,y)
and all of them have a different result. This is actually the same reason why "calling" expressions with positional arguments is deprecated.
If you're still doubting, try and figure out what
(f(x,z)+f(z,y)).substitute_function(f,cos(x)*sin(y+1))
should do.
Thanks for the doctest. Positive review it is.
comment:11 Changed 13 months ago by jdemeyer
- Status changed from positive_review to closed
- Resolution set to fixed
- Merged in set to sage-5.0.beta14

