I have undestood that EPath is similar to xpath, but I am not sure. I want to match all the Sum in an expression. Consider
import sympy
x = sympy.IndexedBase('x', real=True)
i = sympy.Symbol('i', integer=True, positive=True, finite=True)
n = sympy.Symbol('n', integer=True, positive=True, finite=True)
expr = sympy.sin(sympy.Sum(2*x[i], (i, 1, n))) ** 2 + sympy.Sum(x[i], (i, 1, n))
I can match the Sum in the root note with
sympy.EPath(r"/Sum")
(actually it seems wrong to me, /*/Sum
seems more correct). I can match the other one with
sympy.EPath(r"/*/*/Sum")
but I want all of them. From the documentation of xpath I read
//
: Selects nodes in the document from the current node that match the selection no matter where they are
but it seems not supported by sympy:
sympy.EPath(r"//Sum")
---> ValueError: empty selector
If useful I have created a workaround looping on the expression tree:
def traverse_apply(expr, atype, func):
"""
Apply the function `func` to all the element in the expression `expr` of type `atype`.
Return a new expression.
"""
if type(expr) == atype:
return func(expr)
if isinstance(expr, sympy.Basic):
if not expr.is_Atom:
args, basic = expr.args, True
else:
return expr
elif hasattr(expr, '__iter__'):
args, basic = expr, False
else:
return expr
args = list(args)
indices = range(len(args))
for i in indices:
arg = args[i]
args[i] = traverse_apply(arg, atype, func)
if basic:
return expr.func(*args)
else:
return expr.__class__(args)