With
x, a, b, c = symbols ('x a b c')
f = Function ('f') (x)
is there a way to simplify
integrate (f, (x, a, b)) + integrate (f, (x, b, a))
and
integrate (f, (x, a, c)) + integrate (f, (x, c, b)) - integrate (f, (x, a, b))
to zero?
Reference: https://en.wikipedia.org/wiki/Integral#Conventions
The first case can be handled by using a traversal that puts the limits in canonical order:
def iss(i):
if isinstance(i, Integral):
s = 1
lim = list(i.limits)
for j, xab in enumerate(lim):
if len(xab) == 3:
x, a, b = xab
if [a,b]!=list(ordered([a,b])):
lim[j] = (x, b, a)
s *= -1
return i.func(i.function, lim).simplify()*s
return i
>>> eq = integrate (f, (x, a, b)) + integrate (f, (x, b, a))
>>> bottom_up(eq, iss)
0
For the second case there are lots of possible expressions to deal with. But to deal with the type you have shown perhaps the following will work:
def collapse_integrals(a):
from collections import defaultdict
if not a.is_Add: return a
i, d = a.as_independent(Integral)
if not d.is_Add:
return a
if i:
return i + collapse_integrals(d)
igls = defaultdict(list)
other = []
for ai in a.args:
c, i = ai.as_independent(Integral, as_Add=False)
if abs(c) != 1:
other.append(ai)
continue
if not isinstance(i,Integral) or not (len(i.limits) == 1 and len(i.limits[0])==3):
other.append(ai)
else:
igls[(c, i.function)].append(i.limits[0][1:])
for k in igls:
if len(igls[k]) > 1:
n = len(igls[k])
lims = igls[k]
cond = []
nul = (None, None)
for i in range(n):
if lims[i] == nul:continue
for j in range(i + 1, n):
if lims[i][0] == lims[j][1]:
cond.append((lims[j][0],lims[i][1]))
elif lims[i][1] == lims[j][0]:
cond.append((lims[i][0],lims[j][1]))
else:
continue
lims[i] = lims[j] = nul
if cond:
igls[k] = cond + [i for i in lims if i != nul]
c, f = k
other.extend([c*Integral(f, l) for l in igls[k]])
return Add(*other)
>>> eq = integrate (f, (x, a, c)) + integrate (f, (x, c, b)) - integrate (f, (x, a, b))
collapse_integrals(bottom_up(eq,iss))
0