3

I am wondering how to find the symbols connected with Functions in a sympy expression. I am aware of .free_symbols, .atoms(Function) as well as .atoms(AppliedUndef). Here is some code to show why none of these does what I need.

f1 = Function(r'f_1')
f2 = Function(r'f_2')
c1, x = symbols(r'c_1, x')

expr = c1+f1(x)+f2(x)
print(expr)
# c_1 + f_1(x) + f_2(x)
print(expr.free_symbols)
# {x, c_1}
print(expr.atoms(Function))
# {f_1(x), f_2(x)}

from sympy.core.function import AppliedUndef
print(expr.atoms(AppliedUndef))
# {f_1(x), f_2(x)}

(The comments are the outputs of each print line). So .free_symbols is nice, it gives me c_1 and x. However, it does not return the symbols associated with the functions f_1 and f_2. (First question: why? Are they not free somehow?). .atoms(Function) does not help either. It finds the functions, but does not return their associated symbols (e.g. f_1), it returns the whole function call (e.g. f_1(x)).

Main question: How do I find the symbols f_1 and f_2 in above expression?

Background: The reason I want this, is because I would like to lambdify in the following way

expr_num = lambdify([c1, f1, f2, x], expr)

but instead of giving the argument [c1, f1, f2, x] manually, I would like to find all necessary symbols in the expression.

smichr
  • 16,948
  • 2
  • 27
  • 34
Wolpertinger
  • 1,169
  • 2
  • 13
  • 30

2 Answers2

1

The following obtains free symbols and names of AppliedUndef functions:

>>> s = f(x).free_symbols
>>> func = set([i.func for i in f(x).atoms(Function) if isinstance(i, AppliedUndef)])
>>> s | func
{x, f}
smichr
  • 16,948
  • 2
  • 27
  • 34
  • Thanks for your answer! This would solve my problem, but does not run for me. Your code with the definitions of x and f from the question: `x = symbols(r'x') f = Function(r'f') s = f(x).free_symbols func = set([i.name for i in f(x).atoms(Function) if isinstance(i, AppliedUndef)]) s | func` gives `AttributeError: 'f' object has no attribute 'name'`. Indeed `f(x).name` gives the same error. – Wolpertinger Oct 07 '19 at 08:34
  • You might have an old version of SymPy. I updated the answer to use 'func' instead of 'name' since the latter would give a string instead of the function 'f'. – smichr Oct 07 '19 at 13:55
  • The new version with '.func' even works in my sympy version. Great, thanks! – Wolpertinger Oct 07 '19 at 15:41
0

Based on the accepted solution by @smichr, here is a piece of code that can be directly appended to the code in the question (adds nothing interesting, just for convenience):

f1 = Function(r'f_1')
f2 = Function(r'f_2')
c1, x = symbols(r'c_1, x')

syms_and_funs = set(expr.free_symbols) | set([i.func for i in expr.atoms(Function) if isinstance(i, AppliedUndef)])
print(syms_and_funs)
# {x, f_2, f_1, c_1}

expr_num = lambdify(syms_and_funs, expr)
Wolpertinger
  • 1,169
  • 2
  • 13
  • 30