# Ticket #6344: trac_6344-symbolic_derivative_print.patch

File trac_6344-symbolic_derivative_print.patch, 24.3 KB (added by burcin, 10 years ago)

change printing of symbolic derivatives

• ## sage/calculus/calculus.py

# HG changeset patch
# User Burcin Erocal <burcin@erocal.org>
# Date 1247606728 -7200
# Parent  108251386d92cf127cdfab2248d0c0076a2298b0
Change typesetting of derivatives of symbolic functions to something more sensible. #6344

diff --git a/sage/calculus/calculus.py b/sage/calculus/calculus.py
 a sage: f = function('F',x) sage: diff(f*SR(1),x) D[0](F)(x) D[1](F)(x) """ import weakref sage: f = function('f', x) sage: g = f.diff(x); g D[0](f)(x) D[1](f)(x) sage: g.laplace(x, s) s*laplace(f(x), x, s) - f(0) sage: f = function('cr', a) sage: g = f.diff(a).integral(b) sage: g b*D[0](cr)(a) b*D[1](cr)(a) In Sage 4.0, you need to use :meth:substitute_function to replace all occurrences of a function with another:: sage: a = var('a') sage: f = function('cr', a) sage: g = f.diff(a); g D[0](cr)(a) D[1](cr)(a) """ f = args[0] args = list(args[1:])
• ## sage/calculus/tests.py

diff --git a/sage/calculus/tests.py b/sage/calculus/tests.py
 a sage: derivative(x^n, x, 3) (n - 2)*(n - 1)*n*x^(n - 3) sage: derivative( function('f')(x), x) D[0](f)(x) D[1](f)(x) sage: diff( 2*x*f(x^2), x) 4*x^2*D[0](f)(x^2) + 2*f(x^2) 4*x^2*D[1](f)(x^2) + 2*f(x^2) sage: integrate( 1/(x^4 - a^4), x) 1/4*log(-a + x)/a^3 - 1/4*log(a + x)/a^3 - 1/2*arctan(x/a)/a^3 sage: expand(integrate(log(1-x^2), x)) sage: f = function('f'); f f sage: diff(f(x), x) D[0](f)(x) D[1](f)(x) sage: diff(f(x,y), x, y) D[0, 1](f)(x, y) D[1, 1](f)(x, y) sage: diff(f(x,y), x, y) - diff(f(x,y), y, x) 0 sage: g = function('g') sage: var('x y z') (x, y, z) sage: diff(g(x,y,z), x,z,z) D[0, 2, 2](g)(x, y, z) D[1, 0, 2](g)(x, y, z) sage: integrate(sin(x), x) -cos(x) sage: integrate(sin(x), x, 0, pi) sage: function('f, g') (f, g) sage: diff(f(t)*g(t),t) D[0](f)(t)*g(t) + f(t)*D[0](g)(t) D[1](f)(t)*g(t) + f(t)*D[1](g)(t) sage: diff(f(t)/g(t), t) D[0](f)(t)/g(t) - f(t)*D[0](g)(t)/g(t)^2 D[1](f)(t)/g(t) - f(t)*D[1](g)(t)/g(t)^2 sage: diff(f(t) + g(t), t) D[0](f)(t) + D[0](g)(t) D[1](f)(t) + D[1](g)(t) sage: diff(c*f(t), t) c*D[0](f)(t) c*D[1](f)(t) """
• ## sage/calculus/var.pyx

diff --git a/sage/calculus/var.pyx b/sage/calculus/var.pyx
 a sage: g.diff(y) (x, y) |--> 1/2*cos(1/2*y) sage: k = g.diff(x); k (x, y) |--> 2*supersin(x)*D[0](supersin)(x) (x, y) |--> 2*supersin(x)*D[1](supersin)(x) Custom typesetting of symbolic functions in LaTeX:: latex expression:: sage: mu,nu = var('mu,nu') sage: def my_latex_print(*args): return "\\psi_{%s}"%(', '.join(map(latex, args))) sage: def my_latex_print(*args, **kwds): return "\\psi_{%s}"%(', '.join(map(latex, args))) sage: psi(mu,nu) = function('psi', mu, nu, print_latex_func=my_latex_print) sage: latex(psi(mu,nu)) \psi_{\mu, \nu}
• ## sage/functions/other.py

diff --git a/sage/functions/other.py b/sage/functions/other.py
 a approx=math.ceil, conversions=dict(maxima='ceiling')) def _print_latex_(self, x): def _print_latex_(self, x, dparams=None): r""" EXAMPLES: sage: latex(ceil(x)) \left \lceil x \right \rceil sage: latex(ceil(x).derivative(x)) D[1]\left \lceil x \right \rceil """ return r"\left \lceil %s \right \rceil"%latex(x) #FIXME process dparams properly if dparams is not None: dstr = 'D[' + str(dparams[0]) + ']' else: dstr = '' return dstr + r"\left \lceil %s \right \rceil"%latex(x) def __call__(self, x, maximum_bits=20000): try: PrimitiveFunction.__init__(self, "floor", approx=math.floor) def _print_latex_(self, x): def _print_latex_(self, x, dparams=None): r""" EXAMPLES: sage: latex(floor(x)) \left \lfloor x \right \rfloor sage: latex(floor(x).derivative(x)) D[1]\left \lfloor x \right \rfloor """ return r"\left \lfloor %s \right \rfloor"%latex(x) #FIXME process dparams properly if dparams is not None: dstr = 'D[' + str(dparams[0]) + ']' else: dstr = '' return dstr + r"\left \lfloor %s \right \rfloor"%latex(x) def __call__(self, x, maximum_bits=20000): try:
• ## sage/symbolic/expression.pyx

diff --git a/sage/symbolic/expression.pyx b/sage/symbolic/expression.pyx
 a sage: from sage.symbolic.function import function as myfunc sage: foo = myfunc('foo',2) 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) 2*x*D[1, 0](foo)(x^2, x^2) + 2*x*D[0, 1](foo)(x^2, x^2) sage: SR(1)._derivative() 0 sage: f = function('f') sage: a = f(x).diff(x); a D[0](f)(x) D[1](f)(x) sage: a.operator() D[0](f) D[1](f) TESTS: sage: (x <= y).operator() from sage.symbolic.pynac import paramset_from_Expression from sage.symbolic.operators import FDerivativeOperator parameter_set = paramset_from_Expression(self) res = FDerivativeOperator(res, parameter_set) # since we allow creation of symbolic functions with arbitrary # number of arguments, i.e., by specifying 0 as nargs at # creation, we have to extract the number of arguments here res = FDerivativeOperator(res, parameter_set, self._gobj.nops()) return res
• ## sage/symbolic/expression_conversions.py

diff --git a/sage/symbolic/expression_conversions.py b/sage/symbolic/expression_conversions.py
 a sage: from sage.symbolic.expression_conversions import Converter sage: a = function('f', x).diff(x); a D[0](f)(x) D[1](f)(x) sage: Converter().derivative(a, a.operator()) Traceback (most recent call last): ... sage: from sage.symbolic.expression_conversions import InterfaceInit sage: m = InterfaceInit(maxima) sage: a = function('f', x).diff(x); a D[0](f)(x) D[1](f)(x) sage: print m.derivative(a, a.operator()) diff('f(x), x, 1) sage: b = function('f', x).diff(x).diff(x) sage: s = SubstituteFunction(foo(x), foo, bar) sage: f = foo(x).diff(x) sage: s.derivative(f, f.operator()) D[0](bar)(x) D[1](bar)(x) """ if operator.function() is self.original: return operator.change_function(self.new)(*ex.operands())
• ## sage/symbolic/function.pyx

diff --git a/sage/symbolic/function.pyx b/sage/symbolic/function.pyx
 a sage: psi = function('psi', 1)(r); psi psi(r) 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 (r^2*D[2](psi)(r) + 2*r*D[1](psi)(r))/r^2 sage: g.expand() 2*D[0](psi)(r)/r + D[0, 0](psi)(r) 2*D[1](psi)(r)/r + D[2](psi)(r) sage: g.coeff(psi.derivative(r,2)) 1 sage: g.coeff(psi.derivative(r,1)) (y,) {'var': y, 'options': 0, 'at': 0, 'order': 5} y^4 + y^3 + y^2 + y + 1 sage: def my_print(*args): return "my args are: " + ', '.join(map(repr, args)) sage: def my_print(*args, **kwds): return "my args are: " + ', '.join(map(repr, args)) sage: foo = nfunction('t', 2, print_func=my_print) sage: foo(x,y^z) my args are: x, y^z ... ValueError: eval_func parameter must be callable Print function with wrong signature:: sage: def my_print(*args): return 's' sage: foo = nfunction('t', 2, print_func=my_print, print_latex_func=my_print) sage: foo(x,y^z) Traceback (most recent call last): ... TypeError: my_print() got an unexpected keyword argument 'dparams' sage: latex(foo(x,y^z)) Traceback (most recent call last): ... TypeError: my_print() got an unexpected keyword argument 'dparams' Print function returns None:: sage: def my_print(*args, **kwds): return None sage: foo = nfunction('t', 2, print_func=my_print, print_latex_func=my_print) sage: foo(x,y^z) Traceback (most recent call last): ... ValueError: custom print function for symbolic function returned None sage: latex(foo(x,y^z)) Traceback (most recent call last): ... ValueError: custom print function for symbolic function returned None Print function raises error:: sage: def my_print(*args, **kwds): raise ValueError sage: foo = nfunction('t', 2, print_func=my_print, print_latex_func=my_print) sage: foo(x,y^z) Traceback (most recent call last): ... ValueError sage: latex(foo(x,y^z)) Traceback (most recent call last): ... ValueError """ self._name = name self._nargs = nargs
• ## sage/symbolic/operators.py

diff --git a/sage/symbolic/operators.py b/sage/symbolic/operators.py
 a operator.ge:'>='} class FDerivativeOperator(object): def __init__(self, function, parameter_set): def __init__(self, function, parameter_set, nargs): """ EXAMPLES:: sage: from sage.symbolic.operators import FDerivativeOperator sage: f = function('foo') sage: op = FDerivativeOperator(f, [0,1]) sage: op = FDerivativeOperator(f, [0,1], 2) sage: loads(dumps(op)) D[0, 1](foo) D[1, 1](foo) """ self._f = function self._parameter_set = map(int, parameter_set) self._nargs = nargs def __call__(self, *args): """ sage: from sage.symbolic.operators import FDerivativeOperator sage: x,y = var('x,y') sage: f = function('foo') sage: op = FDerivativeOperator(f, [0,1]) sage: op = FDerivativeOperator(f, [0,1], 2) sage: op(x,y) D[0, 1](foo)(x, y) D[1, 1](foo)(x, y) """ if (not all(is_SymbolicVariable(x) for x in args) or len(args) != len(set(args))): sage: from sage.symbolic.operators import FDerivativeOperator sage: f = function('foo') sage: op = FDerivativeOperator(f, [0,1]); op D[0, 1](foo) sage: op = FDerivativeOperator(f, [0,1], 2); op D[1, 1](foo) """ return "D[%s](%s)"%(", ".join(map(repr, self._parameter_set)), self._f) t = [0]*self._nargs for i in self._parameter_set: t[i] += 1 return "D[%s](%s)"%(", ".join(map(repr, t)), self._f) def function(self): """ sage: from sage.symbolic.operators import FDerivativeOperator sage: f = function('foo') sage: op = FDerivativeOperator(f, [0,1]) sage: op = FDerivativeOperator(f, [0,1], 2) sage: op.function() foo """ sage: from sage.symbolic.operators import FDerivativeOperator sage: f = function('foo') sage: b = function('bar') sage: op = FDerivativeOperator(f, [0,1]) sage: op = FDerivativeOperator(f, [0,1], 2) sage: op.change_function(bar) D[0, 1](bar) D[1, 1](bar) """ return FDerivativeOperator(new, self._parameter_set) return FDerivativeOperator(new, self._parameter_set, self._nargs) def parameter_set(self): """ sage: from sage.symbolic.operators import FDerivativeOperator sage: f = function('foo') sage: op = FDerivativeOperator(f, [0,1]) sage: op = FDerivativeOperator(f, [0,1], 2) sage: op.parameter_set() [0, 1] """
• ## sage/symbolic/pynac.pyx

diff --git a/sage/symbolic/pynac.pyx b/sage/symbolic/pynac.pyx
 a # We declare the functions defined below as extern here, to prevent Cython # from generating separate declarations for them which confuse g++ cdef extern from *: stdstring* py_repr(object o, int level) except + stdstring* py_repr(object o, int level) except + stdstring* py_latex(object o, int level) except + stdstring* py_latex_variable(char* var_name) except + stdstring* py_print_function(unsigned id, object args) except + print(ostr.c_str()) stdstring_delete(ostr) def py_print_function_pystring(id, args, fname_paren=False): def dparams_to_str(params): """ Helper function to typeset parameters for symbolic derivatives. INPUT: params - derivative parameters indicating how many times this expression is derivated w.r.t. the corresponding argument EXAMPLES:: sage: from sage.symbolic.pynac import dparams_to_str sage: dparams_to_str((1,)) "'" sage: dparams_to_str((2,)) "''" sage: dparams_to_str((3,)) '^{(3)}' sage: dparams_to_str((1,0)) '^{(1,0)}' """ if len(params) == 1: if params[0] == 1: return "'" elif params[0] == 2: return "''" return ''.join(['^{(', ','.join(map(str, params)), ')}']) def py_print_function_pystring(id, args, dparams=None): """ Return a string with the representation of the symbolic function specified by the given id applied to args. INPUT: INPUT:: id --   serial number of the corresponding symbolic function params -- Set of parameter numbers with respect to which to take the derivative. args -- arguments of the function. dparams -- list of integers indicating how many times the function is derivated w.r.t. each argument EXAMPLES:: True sage: py_print_function_pystring(i, (x,y)) 'foo(x, y)' sage: py_print_function_pystring(i, (x,y), True) '(foo)(x, y)' sage: def my_print(*args): return "my args are: " + ', '.join(map(repr, args)) sage: py_print_function_pystring(i, (x,y), [0,1]) 'D[0, 1](foo)(x, y)' sage: def my_print(*args, **kwds): return "my args are: " + ', '.join(map(repr, args)) sage: foo = function('foo', 2, print_func=my_print) sage: for i in range(get_ginac_serial(), get_ginac_serial()+50): ...     if get_sfunction_from_serial(i) == foo: break True sage: py_print_function_pystring(i, (x,y)) 'my args are: x, y' Test error message:: sage: def broken_print(*args): return "my args are: " + ', '.join(map(repr, args)) sage: bar = function('bar', 2, print_func=broken_print) sage: for i in range(get_ginac_serial(), get_ginac_serial()+50): ...     if get_sfunction_from_serial(i) == bar: break sage: get_sfunction_from_serial(i) == bar True sage: py_print_function_pystring(i, (x,y^z)) Traceback (most recent call last): ... TypeError: broken_print() got an unexpected keyword argument 'dparams' """ cdef SFunction func = get_sfunction_from_serial(id) # This function is called from two places, from function::print in pynac # if function has a custom print function call it if func._print_ is not None: res = func._print_(*args) res = func._print_(*args, dparams=dparams) # make sure the output is a string if res is None: return "" raise ValueError, "custom print function for symbolic function returned None" if not isinstance(res, str): return str(res) res = str(res) return res # otherwise use default output if fname_paren: olist = ['(', func._name, ')'] if dparams: olist = ['D[',', '.join(map(str, dparams)), '](', func._name, ')' ] else: olist = [func._name] olist.extend(['(', ', '.join(map(repr, args)), ')']) cdef public stdstring* py_print_function(unsigned id, object args) except +: return string_from_pystr(py_print_function_pystring(id, args)) def py_latex_function_pystring(id, args, fname_paren=False): def py_latex_function_pystring(id, args, dparams=None): """ Return a string with the latex representation of the symbolic function specified by the given id applied to args. True sage: py_latex_function_pystring(i, (x,y^z)) '{\\rm foo}\\left(x, y^{z}\\right)' sage: py_latex_function_pystring(i, (x,y^z), True) '\\left({\\rm foo}\\right)\\left(x, y^{z}\\right)' sage: py_latex_function_pystring(i, (x,y^z), [1,1]) '{\\rm foo}^{(1,1)}\\left(x, y^{z}\\right)' Test latex_name:: Test custom func:: sage: def my_print(*args): return "my args are: " + ', '.join(map(repr, args)) sage: def my_print(*args, **kwds): return "my args are: " + ', '.join(map(repr, args)) sage: foo = function('foo', 2, print_latex_func=my_print) sage: for i in range(get_ginac_serial(), get_ginac_serial()+50): ...     if get_sfunction_from_serial(i) == foo: break sage: py_latex_function_pystring(i, (x,y^z)) 'my args are: x, y^z' Test error message:: sage: def broken_print(*args): return "my args are: " + ', '.join(map(repr, args)) sage: bar = function('bar', 2, print_latex_func=broken_print) sage: for i in range(get_ginac_serial(), get_ginac_serial()+50): ...     if get_sfunction_from_serial(i) == bar: break sage: get_sfunction_from_serial(i) == bar True sage: py_latex_function_pystring(i, (x,y^z)) Traceback (most recent call last): ... TypeError: broken_print() got an unexpected keyword argument 'dparams' """ cdef SFunction func = get_sfunction_from_serial(id) # This function is called from two places, from function::print in pynac # if function has a custom print function call it if func._print_latex_: res = func._print_latex_(*args) res = func._print_latex_(*args, dparams=dparams) # make sure the output is a string if res is None: return "" raise ValueError, "custom print function for symbolic function returned None" if not isinstance(res, str): return str(res) res = str(res) return res # otherwise, use the latex name if defined # latex_variable_name with "is_fname=True" flag from sage.misc.latex import latex_variable_name name = latex_variable_name(func._name, is_fname=True) if fname_paren: olist = [r'\left(', name, r'\right)'] if dparams: olist = [name, dparams_to_str(dparams)] else: olist = [name] # print the arguments """ ostr = ''.join(['D[', ', '.join([repr(int(x)) for x in params]), ']']) fstr = py_print_function_pystring(id, args, True) py_res = ostr + fstr dparams = [0]*len(args) for i in params: dparams[i] += 1 py_res = py_print_function_pystring(id, args, dparams) return string_from_pystr(py_res) def py_print_fderivative_for_doctests(id, params, args): sage: get_sfunction_from_serial(i) == foo True sage: py_print_fderivative(i, (0, 1, 0, 1), (x, y^z)) D[0, 1, 0, 1](foo)(x, y^z) D[2, 2](foo)(x, y^z) Test custom print function:: sage: def my_print(*args): return "func_with_args(" + ', '.join(map(repr, args)) +')' sage: def my_print(*args, **kwds): return "func_with_args(" + ', '.join(map(repr, args)) +')' sage: foo = function('foo', 2, print_func=my_print) sage: for i in range(get_ginac_serial(), get_ginac_serial()+50): ...     if get_sfunction_from_serial(i) == foo: break sage: get_sfunction_from_serial(i) == foo True sage: py_print_fderivative(i, (0, 1, 0, 1), (x, y^z)) D[0, 1, 0, 1]func_with_args(x, y^z) func_with_args(x, y^z) """ cdef stdstring* ostr = py_print_fderivative(id, params, args) if ostr is NULL: raise RuntimeError, "py_latex_fderivative raised an error" print(ostr.c_str()) stdstring_delete(ostr) See documentation of py_print_fderivative for more information. """ ostr = ''.join(['D[', ', '.join([repr(int(x)) for x in params]), ']']) fstr = py_latex_function_pystring(id, args, True) py_res = ostr + fstr dparams = [0]*len(args) for i in params: dparams[i] += 1 py_res = py_latex_function_pystring(id, args, dparams) return string_from_pystr(py_res) def py_latex_fderivative_for_doctests(id, params, args): sage: get_sfunction_from_serial(i) == foo True sage: py_latex_fderivative(i, (0, 1, 0, 1), (x, y^z)) D[0, 1, 0, 1]\left({\rm foo}\right)\left(x, y^{z}\right) {\rm foo}^{(2,2)}\left(x, y^{z}\right) Test latex_name:: sage: get_sfunction_from_serial(i) == foo True sage: py_latex_fderivative(i, (0, 1, 0, 1), (x, y^z)) D[0, 1, 0, 1]\left(\mathrm{bar}\right)\left(x, y^{z}\right) \mathrm{bar}^{(2,2)}\left(x, y^{z}\right) Test custom func:: sage: def my_print(*args): return "func_with_args(" + ', '.join(map(repr, args)) +')' sage: def my_print(*args, **kwds): return "func_with_args(" + ', '.join(map(repr, args)) +')' sage: foo = function('foo', 2, print_latex_func=my_print) sage: for i in range(get_ginac_serial(), get_ginac_serial()+50): ...     if get_sfunction_from_serial(i) == foo: break sage: get_sfunction_from_serial(i) == foo True sage: py_latex_fderivative(i, (0, 1, 0, 1), (x, y^z)) D[0, 1, 0, 1]func_with_args(x, y^z) func_with_args(x, y^z) """ cdef stdstring* ostr = py_latex_fderivative(id, params, args) if ostr is NULL: raise RuntimeError, "py_latex_fderivative raised an error" print(ostr.c_str()) stdstring_delete(ostr)