On Sun, Feb 24, 2008 at 3:37 PM, David Harvey <[EMAIL PROTECTED]> wrote: > > After hearing some ideas on IRC regarding the derivatives mess, in > this email I propose a plan. It's rough around the edges. Comments > welcome. > > > CURRENT SITUATION > > There are currently at least 18 different functions for > differentiation in Sage, attached to polynomials, power series, > symbolics, and other things. (See my previous email for a complete > list.) > > There are about 4 "diff", 12 "derivative", one "differentiate", and > sometimes (but not always) these names are aliases to each other. > > I count six different call signatures (all "derivative"s replaced by > "diff" in the following list): > > def diff(self): mainly for univariate polynomial and power series types. > > def diff(self, verbose=None): only in the sage.functions.elementary > module. All doctests are switched off on this file. The documentation > says that this function *integrates* things! The verbose flag seems > to be something passed to maxima. > > def diff(self, *args): only in calculus.calculus.SymbolicExpression. > This is the most powerful version, and is the closest to what I want > to eventually support everywhere. > > def diff(f, *args, **kwds): only in calculus.functional. This is a > global, and just dispatches to the diff method on f. > > def diff(self, variable, have_ring=False): in the libsingular > multivariate polynomial code, and also mysteriously crops up in > polynomial_modn_dense_ntl.pyx. The parameter "have_ring" seems to be > ignored everywhere, and somewhere claims that it is needed for > "compatibility reasons". But I can't figure out what this means. > > def diff(self, var='x', n=1): only in > interfaces.maxima.MaximaElement. It means differentiate n times with > respect to x. Note that the variable is supplied as a *string*. We > might want to leave this one out of the discussion; I guess here the > diff function is supposed to conform to maxima semantics (about which > I know nothing) rather than fit into the sage object model. > > There are three open tickets on this issue: > > http://trac.sagemath.org/sage_trac/ticket/756 (patch is problematic) > http://trac.sagemath.org/sage_trac/ticket/753 (not much interesting > here) > http://trac.sagemath.org/sage_trac/ticket/1578 (there's a patch here, > might need to get superseded, sorry Nick) > > There are two separate issues here: function naming, and function > signatures. The former is much simpler, so I'll discuss that first. > > > FUNCTION NAMING > > I propose that we deprecate "differentiate". > > I propose that we use "diff" as the basic name, and have "derivative" > as an alias for any object supporting "diff". > > I propose that there be a lower-level function "_diff" with a simpler > signature, see below. > > > FUNCTION SIGNATURES > > Here is an approximation to what I would like to see. > > Any object F supporting differentiation should have a function > "_diff" (def or cpdef) taking at least one argument var=None. It can > have additional arguments, but these must be optional. For example: > def _diff(self, var=None) > def _diff(self, var=None, have_ring=False) > def _diff(self, var=None, verbose=False) > > If var is supplied, it should be a variable object (for example, a > symbolic variable, or a generator of a polynomial ring). It need not > lie in the parent of F. Examples: > > * If F is in S[x] for some ring S, and you call F._diff(x), you get > what you expect. > * If F is in S[x] for some ring S, and you call F._diff(y), then > G._diff(y) gets called for each coefficient of F. > * If F is in the symbolic ring, then var can be any symbolic variable. > > If var is None, the object makes a decision about the default > variable and uses that. For example: > > * a univariate polynomial or power series will differentiate w.r.t. > the generator. > * a symbolic expression containing precisely one variable will use > that variable. > * a multivariate polynomial will raise an exception. > * a symbolic expression containing more than one variable will raise > an exception. > > Now we come to the "diff" method. It must have an "*args"-style > argument, which must be interpreted according to the following list > of examples (which is almost clear enough to serve as a > specification :-)): > > F.diff(): equivalent to F._diff(None) > F.diff(2): equivalent to F._diff(None)._diff(None) > F.diff(x): equivalent to F._diff(x) > F.diff(x, 3): equivalent to F._diff(x)._diff(x)._diff(x) > F.diff(x, y): equivalent to F._diff(x)._diff(y) > F.diff(x, 3, y, 2, z): equivalent to F._diff(x)._diff(x)._diff > (x)._diff(y)._diff(y)._diff(z) > F.diff(2, x): equivalent to F._diff(None)._diff(None)._diff(x) > [this one currently causes an infinite loop for symbolic objects!] > F.diff([x, y, z]): equivalent to F._diff(x)._diff(y)._diff(z) > F.diff((x, y, z)): equivalent to F._diff(x)._diff(y)._diff(z) > > For the list and tuple versions, it must be the only parameter, and > repetition counts are not allowed. > > When I say "equivalent" in the above descriptions, I don't mean it > literally has to call _diff that way, I just mean the behaviour > should be equivalent. > > Discussion on IRC concluded that partial derivatives need not > commute, so we always have to do the derivatives in the order > specified. (Of course implementations are free to use commutativity > if they know about it, but the general case shouldn't assume it.) > > To avoid code duplication, there will be two global helper functions. > > The first one is > def diff_parse(args) > which takes a list/tuple like [2, x, 3, y] and converts it to a list/ > tuple like [None, None, x, x, x, y]. > > The second one is > def multi_diff(F, args): > for arg in args: > F = F._diff(arg) > return F > > Therefore it will always be possible to implement diff via just: > def diff(self, *args): > return multi_diff(self, diff_parse(args)) > as long as _diff has been implemented (which it should have been!) > > Of course you can put in a more efficient implementation if you want, > for example for univariate polynomials it might be: > def diff(self, *args): > if not args: > return self._diff() > return multi_diff(self, diff_parse(args)) > > And of course a caller is always free to directly call _diff instead > of diff, if they know exactly what they want. > > NOTE: conspicuously absent is the **kwds parameter in the above spec. > I don't know quite how to do this in an efficient way. I'm worried > the overhead will kill us in the last example. Any thoughts about > this are welcome.
Yes, I completely agree with everything. Ondrej --~--~---------~--~----~------------~-------~--~----~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://www.sagemath.org -~----------~----~----~----~------~----~------~--~---