Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Step Derivation implemented #2933

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions sympy/core/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,9 @@ def as_coeff_Add(self):
def _eval_derivative(self, s):
return self.func(*[f.diff(s) for f in self.args])

def _step_derivative(self, s):
return self.func(*[f.diff(s, evaluate=False, step=False) for f in self.args])

def _eval_nseries(self, x, n, logx):
terms = [t.nseries(x, n=n, logx=logx) for t in self.args]
return self.func(*terms)
Expand Down
5 changes: 5 additions & 0 deletions sympy/core/expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -3075,6 +3075,11 @@ def _eval_derivative(self, s):
return S.One
return S.Zero

def _step_derivative(self, s):
if self==s:
return S.One
return S.Zero

def _eval_is_polynomial(self, syms):
return True

Expand Down
49 changes: 48 additions & 1 deletion sympy/core/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,21 @@ def _eval_derivative(self, s):
l.append(df * da)
return Add(*l)

def _step_derivative(self, s):
i = 0
l = []
for a in self.args:
i += 1
da = a.diff(s, evaluate=False)
if da is S.Zero:
continue
try:
df = self.fdiff(i)
except ArgumentIndexError:
df = Function.fdiff(self, i)
l.append(df * da)
return Add(*l)

def _eval_is_commutative(self):
return fuzzy_and(a.is_commutative for a in self.args)

Expand Down Expand Up @@ -1042,6 +1057,7 @@ def __new__(cls, expr, *variables, **assumptions):
# Pop evaluate because it is not really an assumption and we will need
# to track it carefully below.
evaluate = assumptions.pop('evaluate', False)
step = assumptions.pop('step', False)

# Look for a quick exit if there are symbols that don't appear in
# expression at all. Note, this cannnot check non-symbols like
Expand Down Expand Up @@ -1099,7 +1115,10 @@ def __new__(cls, expr, *variables, **assumptions):
expr = expr.subs(v, new_v)
old_v = v
v = new_v
obj = expr._eval_derivative(v)
if step:
obj = expr._step_derivative(v)
else:
obj = expr._eval_derivative(v)
nderivs += 1
if not is_symbol:
if obj is not None:
Expand Down Expand Up @@ -1219,6 +1238,34 @@ def _eval_derivative(self, v):
# couldn't be or evaluate=False originally.
return self.func(self.expr, *(self.variables + (v, )), evaluate=False)

def goto_child(self, infunc):
if len(infunc.args) == 0:
return infunc
temp = infunc
infunc = list(infunc.args)
for i in range(len(infunc)):
if isinstance(infunc[i], Derivative):
infunc[i] = diff(infunc[i].expr, infunc[i].args[-1], evaluate=True, step=True)
else:
infunc[i] = self.goto_child(infunc[i])
at = tuple(infunc)
return temp.func(*at)

def _eval_steps(self):
from sympy.printing.pretty import pretty
a = self.expr.diff(self.variables[0], evaluate=False, step=True)
ans = pretty(a)
a = a.expr.diff(self.variables[0], evaluate=True, step=True)
ans = ans + '\n' + pretty(a)
while(1):
temp = a
a = self.goto_child(a)
if(temp == a):
break
else:
ans = ans + '\n' + pretty(a)
return ans

def doit(self, **hints):
expr = self.expr
if hints.get('deep', True):
Expand Down
10 changes: 10 additions & 0 deletions sympy/core/mul.py
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,16 @@ def _eval_derivative(self, s):
factors.append(self.func(*(terms[:i] + [t] + terms[i + 1:])))
return Add(*factors)

def _step_derivative(self, s):
terms = list(self.args)
factors = []
for i in xrange(len(terms)):
t = terms[i].diff(s)
if t is S.Zero:
continue
factors.append(self.func(*(terms[:i] + [terms[i].diff(s, evaluate=False, step=False)] + terms[i + 1:])))
return Add(*factors)

def _matches_simple(self, expr, repl_dict):
# handle (w*3).matches('x*5') -> {w: x*5/3}
coeff, terms = self.as_coeff_Mul()
Expand Down
5 changes: 5 additions & 0 deletions sympy/core/power.py
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,11 @@ def _eval_derivative(self, s):
dexp = self.exp.diff(s)
return self * (dexp * C.log(self.base) + dbase * self.exp/self.base)

def _step_derivative(self, s):
dbase = self.base.diff(s, evaluate=False)
dexp = self.exp.diff(s, evaluate=False)
return self * (dexp * C.log(self.base) + dbase * self.exp/self.base)

def _eval_evalf(self, prec):
base, exp = self.as_base_exp()
base = base._evalf(prec)
Expand Down
100 changes: 100 additions & 0 deletions sympy/printing/pretty/tests/test_pretty.py
Original file line number Diff line number Diff line change
Expand Up @@ -4717,3 +4717,103 @@ def test_Tr():
def test_pretty_Add():
eq = Mul(-2, x - 2, evaluate=False) + 5
assert pretty(eq) == '-2*(x - 2) + 5'

def test_pretty_steps_Derivative():
ucode = \
u("""\
d \n\
──(cos(x))\n\
dx \n\
d \n\
-sin(x)⋅──(x)\n\
dx \n\
-sin(x)\
""")
assert cos(x).diff(x, evaluate=False)._eval_steps() == ucode

ucode = \
u("""\
d \n\
──(sin(x)⋅cos(x))\n\
dx \n\
d d \n\
sin(x)⋅──(cos(x)) + cos(x)⋅──(sin(x))\n\
dx dx \n\
2 d 2 d \n\
- sin (x)⋅──(x) + cos (x)⋅──(x)\n\
dx dx \n\
2 2 \n\
- sin (x) + cos (x)\
""")
assert (cos(x) * sin(x)).diff(x, evaluate=False)._eval_steps() == ucode

ucode = \
u("""\
d \n\
──(sin(x) + cos(x))\n\
dx \n\
d d \n\
──(sin(x)) + ──(cos(x))\n\
dx dx \n\
d d \n\
- sin(x)⋅──(x) + cos(x)⋅──(x)\n\
dx dx \n\
-sin(x) + cos(x)\
""")
assert (cos(x) + sin(x)).diff(x, evaluate=False)._eval_steps() == ucode
ucode = \
u("""\
d \n\
──(sin(x)⋅cos(2⋅x))\n\
dx \n\
d d \n\
sin(x)⋅──(cos(2⋅x)) + cos(2⋅x)⋅──(sin(x))\n\
dx dx \n\
d d \n\
- sin(x)⋅sin(2⋅x)⋅──(2⋅x) + cos(x)⋅cos(2⋅x)⋅──(x)\n\
dx dx \n\
d \n\
- 2⋅sin(x)⋅sin(2⋅x)⋅──(x) + cos(x)⋅cos(2⋅x)\n\
dx \n\
-2⋅sin(x)⋅sin(2⋅x) + cos(x)⋅cos(2⋅x)\
""")

assert (cos(x * 2) * sin(x)).diff(x, evaluate=False)._eval_steps() == ucode
ucode = \
u("""\
d ⎛ ⎛ 2⎞⎞\n\
──⎝cos⎝x ⎠⎠\n\
dx \n\
⎛ 2⎞ d ⎛ 2⎞\n\
-sin⎝x ⎠⋅──⎝x ⎠\n\
dx \n\
⎛ d ⎞ \n\
⎜ 2⋅──(x)⎟ \n\
2 ⎜ d dx ⎟ ⎛ 2⎞\n\
-x ⋅⎜log(x)⋅──(2) + ───────⎟⋅sin⎝x ⎠\n\
⎝ dx x ⎠ \n\
⎛ 2⎞\n\
-2⋅x⋅sin⎝x ⎠\
""")

assert cos(x**2).diff(x, evaluate=False)._eval_steps() == ucode
ucode = \
u("""\
d ⎛ ⎛ 2⎞⎞\n\
──⎝sin(2⋅x) + cos⎝x ⎠⎠\n\
dx \n\
d d ⎛ ⎛ 2⎞⎞\n\
──(sin(2⋅x)) + ──⎝cos⎝x ⎠⎠\n\
dx dx \n\
⎛ 2⎞ d ⎛ 2⎞ d \n\
- sin⎝x ⎠⋅──⎝x ⎠ + cos(2⋅x)⋅──(2⋅x)\n\
dx dx \n\
⎛ d ⎞ \n\
⎜ 2⋅──(x)⎟ \n\
2 ⎜ d dx ⎟ ⎛ 2⎞ d \n\
- x ⋅⎜log(x)⋅──(2) + ───────⎟⋅sin⎝x ⎠ + 2⋅cos(2⋅x)⋅──(x)\n\
⎝ dx x ⎠ dx \n\
⎛ 2⎞ \n\
- 2⋅x⋅sin⎝x ⎠ + 2⋅cos(2⋅x)\
""")
assert (cos(x**2) + sin(x*2)).diff(x, evaluate=False)._eval_steps() == ucode