# HG changeset patch
# User Burcin Erocal <burcin@erocal.org>
# Date 1237912298 -3600
# Node ID 20f95d9863f3fb92537cee7588d5e1ce9e6d56e1
# Parent  99916902b1d1b0674315779687b6d23133bb899f
Make sage.symbolic.expression.Expression conform to the .derivative() syntax.
This makes jacobian() work on pynac expressions, and resolves #5546.

diff --git a/sage/symbolic/expression.pyx b/sage/symbolic/expression.pyx
--- a/sage/symbolic/expression.pyx
+++ b/sage/symbolic/expression.pyx
@@ -78,6 +78,13 @@
     sage: expand((u + v + a + b + c)^2)
     a^2 + 2*a*b + 2*a*c + b^2 + 2*b*c + c^2 + (2*u + 2*v)*a + (2*u + 2*v)*b + (2*u + 2*v)*c + u^2 + 2*u*v + v^2
 
+TESTS:
+    Test jacobian on pynac expressions. #5546 ::
+    sage: var('x,y', ns=1)
+    (x, y)
+    sage: f = x + y
+    sage: jacobian(f, [x,y])
+    [1 1]
     
 """
 
@@ -96,6 +103,8 @@
 from sage.rings.rational import Rational  # Used for sqrt.
 
 from sage.calculus.calculus import CallableSymbolicExpressionRing
+        
+from sage.misc.derivative import multi_derivative
 
 cdef class Expression(CommutativeRingElement):
     cpdef object pyobject(self):
@@ -763,7 +772,48 @@
             x = g_pow(self._gobj, nexp._gobj)
         return new_Expression_from_GEx(x)
 
-    def diff(self, symb, deg=1):
+    def derivative(self, *args):
+        """
+        Returns the derivative of this expressions with respect to the variables
+        supplied in args.
+
+        Multiple variables and iteration counts may be supplied; see
+        documentation for the global derivative() function for more details.
+
+        .. seealso::
+
+            :meth:`_derivative`
+
+        EXAMPLES::
+            sage: var("x y", ns=1)
+            (x, y)
+            sage: t = (x^2+y)^2
+            sage: t.derivative(x)
+            4*(x^2 + y)*x
+            sage: t.derivative(x, 2)
+            12*x^2 + 4*y
+            sage: t.derivative(x, 2, y)
+            4
+            sage: t.derivative(y)
+            2*x^2 + 2*y
+
+        ::
+
+            sage: t = sin(x+y^2)*tan(x*y)
+            sage: t.derivative(x)
+            (tan(x*y)^2 + 1)*y*sin(y^2 + x) + cos(y^2 + x)*tan(x*y)
+            sage: t.derivative(y)
+            (tan(x*y)^2 + 1)*x*sin(y^2 + x) + 2*y*cos(y^2 + x)*tan(x*y)
+
+        TESTS:
+            sage: t.derivative()
+            Traceback (most recent call last):
+            ...
+            ValueError: No differentiation variable specified.
+        """
+        return multi_derivative(self, args)
+
+    def _derivative(self, symb=None, deg=1):
         """
         Return the deg-th (partial) derivative of self with respect to symb.
         
@@ -771,14 +821,26 @@
             sage: var("x y", ns=1)
             (x, y)
             sage: b = (x+y)^5
-            sage: b.diff(x, 2)
+            sage: b._derivative(x, 2)
             20*(x + y)^3
 
             sage: from sage.symbolic.function import function as myfunc
             sage: foo = myfunc('foo',2)
-            sage: foo(x^2,x^2).diff(x)
+            sage: foo(x^2,x^2)._derivative(x)
             2*x*D[0](foo)(x^2,x^2) + 2*x*D[1](foo)(x^2,x^2)
+
+        TESTS:
+            Raise error if no variable is specified::
+            sage: b._derivative()
+            Traceback (most recent call last):
+            ...
+            ValueError: No differentiation variable specified.
         """
+        if symb is None:
+            # we specify a default value of None for symb and check for it here
+            # to return more helpful error messages when no variable is
+            # given by the multi_derivative framework
+            raise ValueError, "No differentiation variable specified."
         if not isinstance(deg, (int, long, sage.rings.integer.Integer)) \
                 or deg < 1:
             raise TypeError, "argument deg should be an integer >1."
diff --git a/sage/symbolic/function.pyx b/sage/symbolic/function.pyx
--- a/sage/symbolic/function.pyx
+++ b/sage/symbolic/function.pyx
@@ -36,13 +36,13 @@
         (r, kappa)
         sage: psi = function('psi', 1)(r); psi
         psi(r)
-        sage: g = 1/r^2*(2*r*psi.diff(r,1) + r^2*psi.diff(r,2)); g
+        sage: g = 1/r^2*(2*r*psi.derivative(r,1) + r^2*psi.derivative(r,2)); g
         (r^2*D[0,0](psi)(r) + 2*r*D[0](psi)(r))/r^2
         sage: g.expand()
         2*D[0](psi)(r)/r + D[0,0](psi)(r)
-        sage: g.coeff(psi.diff(r,2))
+        sage: g.coeff(psi.derivative(r,2))
         1
-        sage: g.coeff(psi.diff(r,1))
+        sage: g.coeff(psi.derivative(r,1))
         2/r
     """
     cdef unsigned int serial
@@ -89,7 +89,7 @@
 
             sage: def deriv(*args,**kwds): print args, kwds; return args[kwds['diff_param']]^2
             sage: foo = nfunction("foo", 2, derivative_func=deriv)
-            sage: foo(x,y).diff(y)
+            sage: foo(x,y).derivative(y)
             (x, y) {'diff_param': 1}
             y^2
 
