Skip to content

Commit

Permalink
Merge pull request #1131 from skirpichev/diff-syntax
Browse files Browse the repository at this point in the history
Change diff() & co syntax: diff(f, x, 2) -> diff(f, (x, 2)))
  • Loading branch information
skirpichev committed Mar 19, 2021
2 parents 50d4241 + 5baaf0d commit f609b95
Show file tree
Hide file tree
Showing 40 changed files with 276 additions and 336 deletions.
2 changes: 1 addition & 1 deletion diofant/calculus/finite_diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ def as_finite_diff(derivative, points=1, x0=None, wrt=None):
>>> e, sq2 = exp(1), sqrt(2)
>>> xl = [x-h, x+h, x+e*h]
>>> as_finite_diff(f(x).diff(x, 1), xl, x+h*sq2)
>>> as_finite_diff(f(x).diff((x, 1)), xl, x+h*sq2)
2*h*f(E*h + x)*((h + sqrt(2)*h)/(2*h) -
(-sqrt(2)*h + h)/(2*h))/((-h + E*h)*(h + E*h)) +
f(-h + x)*(-(-sqrt(2)*h + h)/(2*h) - (-sqrt(2)*h + E*h)/(2*h))/(h +
Expand Down
2 changes: 1 addition & 1 deletion diofant/concrete/summations.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ def fpoint(expr):
break
if k <= n:
s += term
g = g.diff(i, 2, simplify=False)
g = g.diff((i, 2), simplify=False)
return s + iterm, abs(term)

def reverse_order(self, *indices):
Expand Down
6 changes: 3 additions & 3 deletions diofant/core/expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -2481,7 +2481,7 @@ def taylor_term(self, n, x, *previous_terms):
from ..functions import factorial
x = sympify(x)
_x = Dummy('x')
return self.subs({x: _x}).diff(_x, n).subs({_x: x}).subs({x: 0}) * x**n / factorial(n)
return self.subs({x: _x}).diff((_x, n)).subs({_x: x}).subs({x: 0}) * x**n / factorial(n)

def lseries(self, x=None, x0=0, dir='+', logx=None):
"""Wrapper for series yielding an iterator of the terms of the series.
Expand Down Expand Up @@ -2839,10 +2839,10 @@ def canonical_variables(self):
# ################### DERIVATIVE, INTEGRAL, FUNCTIONAL METHODS ################## #
###################################################################################

def diff(self, *symbols, **kwargs):
def diff(self, *args, **kwargs):
"""Alias for :func:`~diofant.core.function.diff`."""
from .function import diff
return diff(self, *symbols, **kwargs)
return diff(self, *args, **kwargs)

###########################################################################
# #################### EXPRESSION EXPANSION METHODS ##################### #
Expand Down
76 changes: 29 additions & 47 deletions diofant/core/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -768,9 +768,9 @@ class Derivative(Expr):
keyword ``simplify`` is set to False.
>>> e = sqrt((x + 1)**2 + x)
>>> diff(e, x, 5, simplify=False).count_ops()
>>> diff(e, (x, 5), simplify=False).count_ops()
136
>>> diff(e, x, 5).count_ops()
>>> diff(e, (x, 5)).count_ops()
30
Ordering of variables:
Expand Down Expand Up @@ -875,9 +875,9 @@ class Derivative(Expr):
The same is true for derivatives of different orders::
>>> diff(f(x), x, 2).diff(diff(f(x), x, 1))
>>> diff(f(x), (x, 2)).diff(diff(f(x), (x, 1)))
0
>>> diff(f(x), x, 1).diff(diff(f(x), x, 2))
>>> diff(f(x), (x, 1)).diff(diff(f(x), (x, 2)))
0
Note, any class can allow derivatives to be taken with respect to itself.
Expand All @@ -891,7 +891,7 @@ class Derivative(Expr):
2*x
>>> Derivative(Derivative(f(x, y), x), y)
Derivative(f(x, y), x, y)
>>> Derivative(f(x), x, 3)
>>> Derivative(f(x), (x, 3))
Derivative(f(x), x, x, x)
>>> Derivative(f(x, y), y, x, evaluate=True)
Derivative(f(x, y), x, y)
Expand Down Expand Up @@ -925,59 +925,41 @@ def _diff_wrt(self):
else:
return False

def __new__(cls, expr, *variables, **assumptions):
def __new__(cls, expr, *args, **assumptions):
from .symbol import Dummy

expr = sympify(expr)

# There are no variables, we differentiate wrt all of the free symbols
# There are no args, we differentiate wrt all of the free symbols
# in expr.
if not variables:
if not args:
variables = expr.free_symbols
args = tuple(variables)
if len(variables) != 1:
from ..utilities.misc import filldedent
raise ValueError(filldedent("""
The variable(s) of differentiation
must be supplied to differentiate %s""" % expr))

# Standardize the variables by sympifying them and making appending a
# count of 1 if there is only one variable: diff(e,x)->diff(e,x,1).
variables = list(sympify(variables))
if not variables[-1].is_Integer or len(variables) == 1:
variables.append(Integer(1))
# Standardize the args by sympifying them and making appending a
# count of 1 if there is only variable: diff(e, x) -> diff(e, (x, 1)).
args = list(sympify(args))
for i, a in enumerate(args):
if not isinstance(a, Tuple):
args[i] = (a, Integer(1))

# Split the list of variables into a list of the variables we are diff
# wrt, where each element of the list has the form (s, count) where
# s is the entity to diff wrt and count is the order of the
# derivative.
variable_count = []
all_zero = True
i = 0
while i < len(variables) - 1: # process up to final Integer
v, count = variables[i: i + 2]
iwas = i
if v._diff_wrt:
# We need to test the more specific case of count being an
# Integer first.
if count.is_Integer:
count = int(count)
i += 2
elif count._diff_wrt:
count = 1
i += 1

if i == iwas: # didn't get an update because of bad input
for v, count in args:
if not v._diff_wrt:
from ..utilities.misc import filldedent
last_digit = int(str(count)[-1])
ordinal = 'st' if last_digit == 1 else 'nd' if last_digit == 2 else 'rd' if last_digit == 3 else 'th'
ordinal = 'st' if count == 1 else 'nd' if count == 2 else 'rd' if count == 3 else 'th'
raise ValueError(filldedent("""
Can\'t calculate %s%s derivative wrt %s.""" % (count, ordinal, v)))

if all_zero and not count == 0:
all_zero = False

if count:
variable_count.append((v, count))
if all_zero:
all_zero = False
variable_count.append(Tuple(v, count))

# We make a special case for 0th derivative, because there is no
# good way to unambiguously print this.
Expand Down Expand Up @@ -1539,18 +1521,18 @@ def _eval_derivative(self, s):
for v, p in zip(self.variables, self.point)])


def diff(f, *symbols, **kwargs):
def diff(f, *args, **kwargs):
"""
Differentiate f with respect to symbols.
This is just a wrapper to unify .diff() and the Derivative class; its
interface is similar to that of integrate(). You can use the same
shortcuts for multiple variables as with Derivative. For example,
diff(f(x), x, x, x) and diff(f(x), x, 3) both return the third derivative
diff(f(x), x, x, x) and diff(f(x), (x, 3)) both return the third derivative
of f(x).
You can pass evaluate=False to get an unevaluated Derivative class. Note
that if there are 0 symbols (such as diff(f(x), x, 0), then the result will
that if there are 0 symbols (such as diff(f(x), (x, 0)), then the result will
be the function (the zeroth derivative), even if evaluate=False.
Examples
Expand All @@ -1560,18 +1542,18 @@ def diff(f, *symbols, **kwargs):
cos(x)
>>> diff(f(x), x, x, x)
Derivative(f(x), x, x, x)
>>> diff(f(x), x, 3)
>>> diff(f(x), (x, 3))
Derivative(f(x), x, x, x)
>>> diff(sin(x)*cos(y), x, 2, y, 2)
>>> diff(sin(x)*cos(y), (x, 2), (y, 2))
sin(x)*cos(y)
>>> type(diff(sin(x), x))
cos
>>> type(diff(sin(x), x, evaluate=False))
<class 'diofant.core.function.Derivative'>
>>> type(diff(sin(x), x, 0))
>>> type(diff(sin(x), (x, 0)))
sin
>>> type(diff(sin(x), x, 0, evaluate=False))
>>> type(diff(sin(x), (x, 0), evaluate=False))
sin
>>> diff(sin(x))
Expand All @@ -1597,7 +1579,7 @@ def diff(f, *symbols, **kwargs):
"""
kwargs.setdefault('evaluate', True)
return Derivative(f, *symbols, **kwargs)
return Derivative(f, *args, **kwargs)


def expand(e, deep=True, modulus=None, power_base=True, power_exp=True,
Expand Down
8 changes: 4 additions & 4 deletions diofant/functions/special/bessel.py
Original file line number Diff line number Diff line change
Expand Up @@ -796,7 +796,7 @@ class airyai(AiryBase):
>>> diff(airyai(z), z)
airyaiprime(z)
>>> diff(airyai(z), z, 2)
>>> diff(airyai(z), (z, 2))
z*airyai(z)
Series expansion is also supported:
Expand Down Expand Up @@ -950,7 +950,7 @@ class airybi(AiryBase):
>>> diff(airybi(z), z)
airybiprime(z)
>>> diff(airybi(z), z, 2)
>>> diff(airybi(z), (z, 2))
z*airybi(z)
Series expansion is also supported:
Expand Down Expand Up @@ -1155,7 +1155,7 @@ class airyaiprime(AiryBase):
>>> diff(airyaiprime(z), z)
z*airyai(z)
>>> diff(airyaiprime(z), z, 2)
>>> diff(airyaiprime(z), (z, 2))
z*airyaiprime(z) + airyai(z)
Series expansion is also supported:
Expand Down Expand Up @@ -1298,7 +1298,7 @@ class airybiprime(AiryBase):
>>> diff(airybiprime(z), z)
z*airybi(z)
>>> diff(airybiprime(z), z, 2)
>>> diff(airybiprime(z), (z, 2))
z*airybiprime(z) + airybi(z)
Series expansion is also supported:
Expand Down
10 changes: 5 additions & 5 deletions diofant/functions/special/gamma_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,22 +502,22 @@ class polygamma(Function):
>>> diff(polygamma(0, x), x)
polygamma(1, x)
>>> diff(polygamma(0, x), x, 2)
>>> diff(polygamma(0, x), (x, 2))
polygamma(2, x)
>>> diff(polygamma(0, x), x, 3)
>>> diff(polygamma(0, x), (x, 3))
polygamma(3, x)
>>> diff(polygamma(1, x), x)
polygamma(2, x)
>>> diff(polygamma(1, x), x, 2)
>>> diff(polygamma(1, x), (x, 2))
polygamma(3, x)
>>> diff(polygamma(2, x), x)
polygamma(3, x)
>>> diff(polygamma(2, x), x, 2)
>>> diff(polygamma(2, x), (x, 2))
polygamma(4, x)
>>> diff(polygamma(n, x), x)
polygamma(n + 1, x)
>>> diff(polygamma(n, x), x, 2)
>>> diff(polygamma(n, x), (x, 2))
polygamma(n + 2, x)
We can rewrite polygamma functions in terms of harmonic numbers:
Expand Down
6 changes: 3 additions & 3 deletions diofant/integrals/rationaltools.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def ratint_ratpart(f, g, x):
f = sympify(f).as_poly(x)
g = sympify(g).as_poly(x)

u, v, _ = g.cofactors(g.diff())
u, v, _ = g.cofactors(g.diff(x))

n = u.degree()
m = v.degree()
Expand All @@ -147,7 +147,7 @@ def ratint_ratpart(f, g, x):
A = Poly(A_coeffs, x, domain=ZZ.inject(*C_coeffs))
B = Poly(B_coeffs, x, domain=ZZ.inject(*C_coeffs))

H = f - A.diff()*v + A*(u.diff()*v).quo(u) - B*u
H = f - A.diff(x)*v + A*(u.diff(x)*v).quo(u) - B*u

result = solve(H.coeffs(), C_coeffs)[0]

Expand Down Expand Up @@ -194,7 +194,7 @@ def ratint_logpart(f, g, x, t=None):
f, g = sympify(f).as_poly(x), sympify(g).as_poly(x)

t = t or Dummy('t')
a, b = g, f - g.diff()*t.as_poly(x)
a, b = g, f - g.diff(x)*t.as_poly(x)

res, R = resultant(a, b, includePRS=True)
res = res.as_poly(t, composite=False)
Expand Down
8 changes: 1 addition & 7 deletions diofant/logic/boolalg.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from ..core import Atom, cacheit
from ..core.expr import Expr
from ..core.function import Application, Derivative
from ..core.function import Application
from ..core.numbers import Number
from ..core.operations import LatticeOp
from ..core.singleton import S
Expand Down Expand Up @@ -899,12 +899,6 @@ def to_nnf(self, simplify=True):
def _eval_derivative(self, x):
return self.func(self.args[0], *[a.diff(x) for a in self.args[1:]])

# the diff method below is copied from Expr class
def diff(self, *symbols, **assumptions):
new_symbols = list(map(sympify, symbols)) # e.g. x, 2, y, z
assumptions.setdefault('evaluate', True)
return Derivative(self, *new_symbols, **assumptions)


# end class definitions. Some useful methods

Expand Down
2 changes: 1 addition & 1 deletion diofant/matrices/dense.py
Original file line number Diff line number Diff line change
Expand Up @@ -1473,7 +1473,7 @@ def wronskian(functions, var, method='bareiss'):
n = len(functions)
if n == 0:
return 1
W = Matrix(n, n, lambda i, j: functions[i].diff(var, j))
W = Matrix(n, n, lambda i, j: functions[i].diff((var, j)))
return W.det(method)


Expand Down
4 changes: 2 additions & 2 deletions diofant/polys/partfrac.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ def apart_list_full_decomposition(P, Q, dummygen):

for d, n in Q_sqf:
b = d.as_expr()
U += [u.diff(x, n - 1)]
U += [u.diff((x, n - 1))]

h = cancel(f*b**n) / u**n

Expand All @@ -381,7 +381,7 @@ def apart_list_full_decomposition(P, Q, dummygen):
H += [H[-1].diff(x) / j]

for j in range(1, n + 1):
subs += [(U[j - 1], b.diff(x, j) / j)]
subs += [(U[j - 1], b.diff((x, j)) / j)]

for j in range(n):
P, Q = cancel(H[j]).as_numer_denom()
Expand Down
36 changes: 4 additions & 32 deletions diofant/polys/polytools.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import mpmath

from ..core import (Add, Basic, Derivative, E, Expr, Integer, Mul, Tuple, oo,
from ..core import (Add, Basic, E, Expr, Integer, Mul, Tuple, oo,
preorder_traversal)
from ..core.compatibility import iterable
from ..core.decorators import _sympifyit
Expand Down Expand Up @@ -1288,40 +1288,12 @@ def integrate(self, *specs, **args):

return f.per(rep)

def diff(self, *specs, **kwargs):
"""
Computes partial derivative of ``self``.
Examples
========
>>> (x**2 + 2*x + 1).as_poly().diff()
Poly(2*x + 2, x, domain='ZZ')
>>> (x*y**2 + x).as_poly().diff((0, 0), (1, 1))
Poly(2*x*y, x, y, domain='ZZ')
"""
if not kwargs.get('evaluate', True):
return Derivative(self, *specs, **kwargs)

if not specs:
return self.per(self.rep.diff())

def _eval_derivative(self, v):
rep = self.rep

for spec in specs:
if type(spec) is tuple:
gen, m = spec
else:
gen, m = spec, 1

rep = rep.diff(self._gen_to_level(gen), int(m))

v = self._gen_to_level(v)
rep = rep.diff(v)
return self.per(rep)

_eval_derivative = diff

def eval(self, x, a=None, auto=True):
"""
Evaluate ``self`` at ``a`` in the given variable.
Expand Down

0 comments on commit f609b95

Please sign in to comment.