Opened 9 years ago
Closed 7 years ago
#12922 closed enhancement (fixed)
Implicit derivative
Reported by:  mmarco  Owned by:  burcin 

Priority:  major  Milestone:  sage6.5 
Component:  calculus  Keywords:  implicit derivative 
Cc:  kcrisman, sjg10, burcin  Merged in:  
Authors:  Miguel Marco  Reviewers:  Kannappan Sampath, Ralf Stephan 
Report Upstream:  N/A  Work issues:  
Branch:  0a76da7 (Commits, GitHub, GitLab)  Commit:  0a76da78806d27c25e8a5864c26b3106611ab940 
Dependencies:  Stopgaps: 
Description
Added implicit_derivative function. It allows to compute the n'th derivative of the implicit function defined by a symbolic expression.
Attachments (3)
Change History (39)
Changed 9 years ago by
comment:1 Changed 9 years ago by
 Status changed from new to needs_review
comment:2 Changed 9 years ago by
 Cc kcrisman added
comment:3 Changed 9 years ago by
comment:4 Changed 9 years ago by
Yes, both helpful! A few comments.
 The reviewer patch should be a separate patch on top of the original patch, so that the original author is kept.
 In any case, the patch should have a meaningful commit message.
 I'm a little apprehensive about the many specific variables and functions defined here. Won't that cause some kind of conflict with (say) an alreadydefined
yy
? We shouldn't depend on people not needing those variables. Or is everything only in local scope because of the use ofSR.symbol
?
I'm also wondering  is it possible to have an implicit derivative method for symbolic expressions? Just curious.
comment:5 Changed 9 years ago by
All the variables defined in the function are local. They do not get mixed with the global variables with the same name. You can even call the function with xx and yy as parameters, and they will not be mixed with the local xx and yy.
sage: var('x,y') (x, y) sage: implicit_derivative(y,x,y*x/cos(x+y)) (x*y*sin(x + y)/cos(x + y)^2 + y/cos(x + y))/(x*y*sin(x + y)/cos(x + y)^2 + x/cos(x + y)) sage: var('xx,yy') (xx, yy) sage: implicit_derivative(yy,xx,yy*xx/cos(xx+yy)) (xx*yy*sin(xx + yy)/cos(xx + yy)^2 + yy/cos(xx + yy))/(xx*yy*sin(xx + yy)/cos(xx + yy)^2 + xx/cos(xx + yy))
About writing this as a method for symbolic expressions... that was my first idea; but i prefeared this aproach, due to the fact that you are actually not diferentiating the symbolic expression, but a function defined implicitely by the symbolic expression. So it seemed more natural to me this kind of syntax.
comment:6 Changed 9 years ago by
Calling var
or SR.symbol('somename')
even locally does add to the pynac_symbol_registry
, yet calling SR.symbol()
with no argument will create a temporary variable that will not be added to this registry. Possibly adding to the registry may cause problems if it is being referenced directly for some reason. Maybe worth just using x = SR.symbol()
etc?
sage: from sage.symbolic.ring import pynac_symbol_registry sage: pynac_symbol_registry {'some_variable': some_variable, 'A': A, 'C': C, 'B': B, 'E': E, 'D': D, 'G': G, 'F': F, 'H': H, 'K': K, 'J': J, 'M': M, 'L': L, 'N': N, 'Q': Q, 'P': P, 'S': S, 'R': R, 'U': U, 'T': T, 'W': W, 'V': V, 'Y': Y, 'X': X, 'Z': Z, 'a': a, 'c': c, 'b': b, 'd': d, 'g': g, 'f': f, 'h': h, 'k': k, 'j': j, 'm': m, 'l': l, 'o': o, 'n': n, 'q': q, 'p': p, 's': s, 'r': r, 'u': u, 't': t, 'w': w, 'v': v, 'y': y, 'x': x, 'z': z} sage: def f(): temp = SR.symbol() ....: sage: f() sage: pynac_symbol_registry {'some_variable': some_variable, 'A': A, 'C': C, 'B': B, 'E': E, 'D': D, 'G': G, 'F': F, 'H': H, 'K': K, 'J': J, 'M': M, 'L': L, 'N': N, 'Q': Q, 'P': P, 'S': S, 'R': R, 'U': U, 'T': T, 'W': W, 'V': V, 'Y': Y, 'X': X, 'Z': Z, 'a': a, 'c': c, 'b': b, 'd': d, 'g': g, 'f': f, 'h': h, 'k': k, 'j': j, 'm': m, 'l': l, 'o': o, 'n': n, 'q': q, 'p': p, 's': s, 'r': r, 'u': u, 't': t, 'w': w, 'v': v, 'y': y, 'x': x, 'z': z} sage: def f(): temp = SR.symbol('temp') ....: sage: f() sage: pynac_symbol_registry {'temp': temp, 'some_variable': some_variable, 'A': A, 'C': C, 'B': B, 'E': E, 'D': D, 'G': G, 'F': F, 'H': H, 'K': K, 'J': J, 'M': M, 'L': L, 'N': N, 'Q': Q, 'P': P, 'S': S, 'R': R, 'U': U, 'T': T, 'W': W, 'V': V, 'Y': Y, 'X': X, 'Z': Z, 'a': a, 'c': c, 'b': b, 'd': d, 'g': g, 'f': f, 'h': h, 'k': k, 'j': j, 'm': m, 'l': l, 'o': o, 'n': n, 'q': q, 'p': p, 's': s, 'r': r, 'u': u, 't': t, 'w': w, 'v': v, 'y': y, 'x': x, 'z': z}
comment:7 Changed 9 years ago by
I have tried to use SR.symbol() instead of SR.symbol('x'), but then the result is a mess.
I can't think of a way to make this work without the said side effect.
comment:8 Changed 8 years ago by
 Cc sjg10 added
I solved the pynac symbol registry pollution by saving a copy of it at the beginning of the function, and restoring it at the end. I am not sure if this is the best choice, but it is the only solution i found, and seems to work fine.
comment:9 Changed 8 years ago by
 Status changed from needs_review to needs_work
Messing with the symbol registry this way is really not a good idea. That is just supposed to be internal data.
What if there is an exception in that code and the registry is not restored?
What exactly is the problem with using temporary variables? Looking briefly at the patch, it looks like we also need temporary functions.
comment:10 followup: ↓ 11 Changed 8 years ago by
The problem with using temporary variables without giving them a name string is this:
sage: from sage.symbolic.function_factory import SymbolicFunction sage: var('x,y') (x, y) sage: def implicit_derivative(Y,X,F,n=1): ....: x=SR.symbol() ....: yy=SR.symbol() ....: y=SymbolicFunction('y',1)(x) ....: f=SymbolicFunction('f',2)(x,yy) ....: Fx=f.diff(x) ....: Fy=f.diff(yy) ....: G=(Fx/Fy) ....: G=G.subs_expr({yy:y}) ....: di={y.diff(x):F.diff(X)/F.diff(Y)} ....: R=G ....: S=G.diff(x,n1) ....: for i in range(n+1): ....: di[y.diff(x,i+1)(x=x)]=R ....: S=S.subs_expr(di) ....: R=G.diff(x,i) ....: for j in range(n+1i): ....: di[f.diff(x,i,yy,j)(x=x,yy=y)]=F.diff(X,i,Y,j) ....: S=S.subs_expr(di) ....: return S ....: sage: implicit_derivative(y,x,x^2+y^21) D[0](f)(symbol160, y(symbol160))/D[1](f)(symbol160, y(symbol160))
I don't know why, this problem is not present if we put the strings in the definitions of the variables.
comment:11 in reply to: ↑ 10 Changed 8 years ago by
Replying to mmarco:
sage: implicit_derivative(y,x,x^2+y^21) D[0](f)(symbol160, y(symbol160))/D[1](f)(symbol160, y(symbol160))
It looks like the substitution doesn't work at some point.
Looking at the code...
The calls y.diff(x,i+1)(x=x)
and f.diff(x,i,yy,j)(x=x,yy=y)
rely on the name of the variable. You should use a dictionary argument to .subs()
.
BTW, the subs_expr()
method will go away at some point. Why not just use .subs()
everywhere?
comment:12 Changed 8 years ago by
Before this gets switched to positive review, we should check how it behaves if there already is a symbolic function defined with a custom method for evaluation named y
or f
.
Why is this not a method of symbolic expressions? Isn't it inconsistent to have the functional notation supported but not a class method?
comment:13 followup: ↓ 27 Changed 8 years ago by
Thanks for the comments. I will try to work on it.
As i said, the reason why it is not a method is that it is not clear to me what class should it be in.
Is this a method of the expression F? Well, not exactly, since F is not the function that we are trying to differentiate, but the symbollic expression that deffines it implicitly.
Of X? This is just the variable with which we differentiate on.
Of Y? Riguorilously that should be the function that we are differentiating. But doesn't sound natural at all to add a method like this for symbollic variables.
Or maybe we could define a new class ImplicitelyDefinedSymbolicFunction? for this case, bat that sounds way overkill.
comment:14 Changed 8 years ago by
Ok, your suggestions seemed to work fine. I have uploaded a newer version. Please check it.
comment:15 Changed 8 years ago by
 Status changed from needs_work to needs_review
comment:16 Changed 8 years ago by
 Milestone changed from sage5.11 to sage5.12
comment:17 Changed 8 years ago by
 Milestone changed from sage6.1 to sage6.2
comment:18 Changed 7 years ago by
 Milestone changed from sage6.2 to sage6.3
comment:19 Changed 7 years ago by
 Milestone changed from sage6.3 to sage6.4
comment:21 Changed 7 years ago by
Hi mmarco:
Thanks for writing up a patch regarding something very fundamental to calculus. Sage has since v6.0 moved to Git for its revision control. As a consequence, we work with "branches" instead of "patches" and so on. Could you kindly make the patch here into a branch?
I think this might be a bit painful depending on several factors. So, please feel free to ask for any help!
comment:22 Changed 7 years ago by
 Status changed from needs_review to needs_work
comment:23 Changed 7 years ago by
 Branch set to u/mmarco/ticket/12922
 Created changed from 05/08/12 11:35:00 to 05/08/12 11:35:00
 Modified changed from 08/23/14 12:10:47 to 08/23/14 12:10:47
comment:24 Changed 7 years ago by
 Commit set to 39aaf0c4b33a7a5ec0066e64e25cf5e54d197247
 Reviewers set to Kannappan Sampath
 Status changed from needs_work to needs_review
New commits:
39aaf0c  Added implicit_derivative

comment:25 Changed 7 years ago by
For starters, I have the following comments:
 The oneline docstring for the function is "incorrect": it computes something, yes, but it also returns it. So, please change that to "Return <whatever>".
 The value error could return the expression
F
explicitly instead of saying the generic F. While you are it, please use the.format
to format the string for the output instead.
Honestly, I have not looked at the math. I will get back about the math and the programming part in a bit.
comment:26 Changed 7 years ago by
 Commit changed from 39aaf0c4b33a7a5ec0066e64e25cf5e54d197247 to 3f3f949cff46cc4e2f5b93bcf41de4c3f81f13cd
Branch pushed to git repo; I updated commit sha1. New commits:
3f3f949  Reviewer's suggestions changed, plus some changes for PEP8 compliance.

comment:27 in reply to: ↑ 13 Changed 7 years ago by
Replying to mmarco:
As i said, the reason why it is not a method is that it is not clear to me what class should it be in.
Is this a method of the expression F? Well, not exactly, since F is not the function that we are trying to differentiate, but the symbollic expression that deffines it implicitly.
I think Burcin wants this method part of the symbolic expression or function class. If it's not tied to a specific expression then make it a @classmethod
(equivalent to a static method in C), and then create the globally accessible name for it.
comment:28 Changed 7 years ago by
 Commit changed from 3f3f949cff46cc4e2f5b93bcf41de4c3f81f13cd to 5fe0ffeca2be3ec698b7eccfeb51ce80fa94a62c
Branch pushed to git repo; I updated commit sha1. New commits:
5fe0ffe  Moved to a method in the expression class

comment:29 Changed 7 years ago by
Moved it to a method in the expression class.
comment:30 Changed 7 years ago by
 Branch changed from u/mmarco/ticket/12922 to u/rws/ticket/12922
comment:31 Changed 7 years ago by
 Commit changed from 5fe0ffeca2be3ec698b7eccfeb51ce80fa94a62c to 954997662dc64a089c3ce6c90d423a78f3117834
 Milestone changed from sage6.4 to sage6.5
 Reviewers changed from Kannappan Sampath to Kannappan Sampath, Ralf Stephan
 Status changed from needs_review to positive_review
comment:32 Changed 7 years ago by
Thanks for the review.
comment:33 Changed 7 years ago by
 Status changed from positive_review to needs_work
********************************************************************** File "src/sage/misc/sageinspect.py", line 1815, in sage.misc.sageinspect.sage_getsourcelines Failed example: sage_getsourcelines(x)[0][1] # last line Expected: ' return self / x\n' Got: ' return S\n' **********************************************************************
comment:34 Changed 7 years ago by
 Commit changed from 954997662dc64a089c3ce6c90d423a78f3117834 to 0a76da78806d27c25e8a5864c26b3106611ab940
Branch pushed to git repo; I updated commit sha1. New commits:
0a76da7  12922: fix doctest

comment:35 Changed 7 years ago by
 Status changed from needs_work to positive_review
Interesting. That doctest depends on the last line in class Expression
to remain unchanged.
comment:36 Changed 7 years ago by
 Branch changed from u/rws/ticket/12922 to 0a76da78806d27c25e8a5864c26b3106611ab940
 Resolution set to fixed
 Status changed from positive_review to closed
Reviewed, seems good. Added a check for having no y term in the expression. Was previously an uncaught
DivideByZero
, while code now gives a more informative error. Was my first review, would appreciate to know whether I'm actually being any help!