Ticket #12801 (closed defect: fixed)

Opened 14 months ago

Last modified 13 months ago

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


Apply 12801.patch Download, sage-trac_12801-review.patch Download.

Attachments

12801.patch Download (857 bytes) - added by nbruin 14 months ago.
sage-trac_12801-review.patch Download (1013 bytes) - added by mjo 14 months ago.
Add a doctest.

Change History

Changed 14 months ago by nbruin

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:3 Changed 14 months ago by mjo

  • Cc mjo added

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.

Changed 14 months ago by mjo

Add a doctest.

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:10 Changed 14 months ago by jdemeyer

  • Authors set to Nils Bruin

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
Note: See TracTickets for help on using tickets.