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

Reply via email to