pythonsympysimplifysymbolic-integration

Simplify sympy integral expressions


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


Solution

  • 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