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. david --~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---