我们从Python开源项目中,提取了以下50个代码示例,用于说明如何使用sympy.Function()。
def _has(self, pattern): """Helper for .has()""" from sympy.core.function import UndefinedFunction, Function if isinstance(pattern, UndefinedFunction): return any(f.func == pattern or f == pattern for f in self.atoms(Function, UndefinedFunction)) pattern = sympify(pattern) if isinstance(pattern, BasicType): return any(isinstance(arg, pattern) for arg in preorder_traversal(self)) try: match = pattern._has_matcher() return any(match(arg) for arg in preorder_traversal(self)) except AttributeError: return any(arg == pattern for arg in preorder_traversal(self))
def test_function_series1(): """Create our new "sin" function.""" class my_function(Function): def fdiff(self, argindex=1): return cos(self.args[0]) @classmethod def eval(cls, arg): arg = sympify(arg) if arg == 0: return sympify(0) #Test that the taylor series is correct assert my_function(x).series(x, 0, 10) == sin(x).series(x, 0, 10) assert limit(my_function(x)/x, x, 0) == 1
def test_function_series2(): """Create our new "cos" function.""" class my_function2(Function): def fdiff(self, argindex=1): return -sin(self.args[0]) @classmethod def eval(cls, arg): arg = sympify(arg) if arg == 0: return sympify(1) #Test that the taylor series is correct assert my_function2(x).series(x, 0, 10) == cos(x).series(x, 0, 10)
def test_function_series3(): """ Test our easy "tanh" function. This test tests two things: * that the Function interface works as expected and it's easy to use * that the general algorithm for the series expansion works even when the derivative is defined recursively in terms of the original function, since tanh(x).diff(x) == 1-tanh(x)**2 """ class mytanh(Function): def fdiff(self, argindex=1): return 1 - mytanh(self.args[0])**2 @classmethod def eval(cls, arg): arg = sympify(arg) if arg == 0: return sympify(0) e = tanh(x) f = mytanh(x) assert tanh(x).series(x, 0, 6) == mytanh(x).series(x, 0, 6)
def test_as_integral(): from sympy import Function, Integral f = Function('f') assert mellin_transform(f(x), x, s).rewrite('Integral') == \ Integral(x**(s - 1)*f(x), (x, 0, oo)) assert fourier_transform(f(x), x, s).rewrite('Integral') == \ Integral(f(x)*exp(-2*I*pi*s*x), (x, -oo, oo)) assert laplace_transform(f(x), x, s).rewrite('Integral') == \ Integral(f(x)*exp(-s*x), (x, 0, oo)) assert str(inverse_mellin_transform(f(s), s, x, (a, b)).rewrite('Integral')) \ == "Integral(x**(-s)*f(s), (s, _c - oo*I, _c + oo*I))" assert str(inverse_laplace_transform(f(s), s, x).rewrite('Integral')) == \ "Integral(f(s)*exp(s*x), (s, _c - oo*I, _c + oo*I))" assert inverse_fourier_transform(f(s), s, x).rewrite('Integral') == \ Integral(f(s)*exp(2*I*pi*s*x), (s, -oo, oo)) # NOTE this is stuck in risch because meijerint cannot handle it
def test_namespace_order(): # lambdify had a bug, such that module dictionaries or cached module # dictionaries would pull earlier namespaces into themselves. # Because the module dictionaries form the namespace of the # generated lambda, this meant that the behavior of a previously # generated lambda function could change as a result of later calls # to lambdify. n1 = {'f': lambda x: 'first f'} n2 = {'f': lambda x: 'second f', 'g': lambda x: 'function g'} f = sympy.Function('f') g = sympy.Function('g') if1 = lambdify(x, f(x), modules=(n1, "sympy")) assert if1(1) == 'first f' if2 = lambdify(x, g(x), modules=(n2, "sympy")) # previously gave 'second f' assert if1(1) == 'first f'
def test_imps(): # Here we check if the default returned functions are anonymous - in # the sense that we can have more than one function with the same name f = implemented_function('f', lambda x: 2*x) g = implemented_function('f', lambda x: math.sqrt(x)) l1 = lambdify(x, f(x)) l2 = lambdify(x, g(x)) assert str(f(x)) == str(g(x)) assert l1(3) == 6 assert l2(3) == math.sqrt(3) # check that we can pass in a Function as input func = sympy.Function('myfunc') assert not hasattr(func, '_imp_') my_f = implemented_function(func, lambda x: 2*x) assert hasattr(func, '_imp_') # Error for functions with same name and different implementation f2 = implemented_function("f", lambda x: x + 101) raises(ValueError, lambda: lambdify(x, f(f2(x))))
def test_dummification(): t = symbols('t') F = Function('F') G = Function('G') #"\alpha" is not a valid python variable name #lambdify should sub in a dummy for it, and return #without a syntax error alpha = symbols(r'\alpha') some_expr = 2 * F(t)**2 / G(t) lam = lambdify((F(t), G(t)), some_expr) assert lam(3, 9) == 2 lam = lambdify(sin(t), 2 * sin(t)**2) assert lam(F(t)) == 2 * F(t)**2 #Test that \alpha was properly dummified lam = lambdify((alpha, t), 2*alpha + t) assert lam(2, 1) == 5 raises(SyntaxError, lambda: lambdify(F(t) * G(t), F(t) * G(t) + 5)) raises(SyntaxError, lambda: lambdify(2 * F(t), 2 * F(t) + 5)) raises(SyntaxError, lambda: lambdify(2 * F(t), 4 * F(t) + 5))
def _extractFunctionsFromSympy(self, expr, top=False): newVars = [] newArgs = [] isExpensive = False for arg in expr.args: (newVarsFromArg, newArg,dontcare) = self._extractFunctionsFromSympy(arg) newVars += newVarsFromArg newArgs.append(newArg) if newVars: expr = expr.func(*newArgs) if isinstance(expr.func, type(sympy.Function)) or ( expr.func == sympy.Pow and ( not expr.exp.is_constant or int(expr.exp) != expr.exp)): if top: isExpensive = True else: newSym = self.addSSA("_expensive_functions", expr) expr = newSym newVars.append(newSym) return (newVars, expr, isExpensive)
def test_variance_prop(): x, y, z = symbols('x y z') phi, t = consts = symbols('phi t') a = RandomSymbol(x) var_x = Variance(a) var_y = Variance(RandomSymbol(y)) var_z = Variance(RandomSymbol(z)) f = Function('f')(x) cases = { x + y: var_x + var_y, a + y: var_x + var_y, x + y + z: var_x + var_y + var_z, 2*x: 4*var_x, x*y: var_x*y**2 + var_y*x**2, 1/x: var_x/x**4, x/y: (var_x*y**2 + var_y*x**2)/y**4, exp(x): var_x*exp(2*x), exp(2*x): 4*var_x*exp(4*x), exp(-x*t): t**2*var_x*exp(-2*t*x), f: Variance(f), } for inp, out in cases.items(): obs = variance_prop(inp, consts=consts) assert out == obs
def test_differentiate_finite(): x, y = symbols('x y') f = Function('f') res0 = differentiate_finite(f(x, y) + exp(42), x, y) xm, xp, ym, yp = [v + sign*S(1)/2 for v, sign in product([x, y], [-1, 1])] ref0 = f(xm, ym) + f(xp, yp) - f(xm, yp) - f(xp, ym) assert (res0 - ref0).simplify() == 0 g = Function('g') res1 = differentiate_finite(f(x)*g(x) + 42, x) ref1 = (-f(x - S(1)/2) + f(x + S(1)/2))*g(x) + \ (-g(x - S(1)/2) + g(x + S(1)/2))*f(x) assert (res1 - ref1).simplify() == 0 res2 = differentiate_finite(f(x) + x**3 + 42, x, points=[x-1, x+1], evaluate=False) ref2 = (f(x + 1) + (x + 1)**3 - f(x - 1) - (x - 1)**3)/2 assert (res2 - ref2).simplify() == 0
def as_symbol(expr): """ Extract the "main" symbol from a SymPy object. """ try: return Number(expr) except (TypeError, ValueError): pass if isinstance(expr, str): return Symbol(expr) elif isinstance(expr, Dimension): return Symbol(expr.name) elif expr.is_Symbol: return expr elif isinstance(expr, Indexed): return expr.base.label elif isinstance(expr, Function): return Symbol(expr.__class__.__name__) else: raise TypeError("Cannot extract symbol from type %s" % type(expr))
def __new__(cls, *args, **kwargs): options = kwargs.get('options', {}) if cls in _SymbolCache: newobj = sympy.Symbol.__new__(cls, *args, **options) newobj._cached_init() else: name = kwargs.get('name') # Create the new Function object and invoke __init__ newcls = cls._symbol_type(name) newobj = sympy.Symbol.__new__(newcls, name, *args, **options) newobj.__init__(*args, **kwargs) # Store new instance in symbol cache newcls._cache_put(newobj) return newobj
def __new__(cls, *args, **kwargs): if cls in _SymbolCache: options = kwargs.get('options', {}) newobj = sympy.Function.__new__(cls, *args, **options) newobj._cached_init() else: name = kwargs.get('name') if len(args) < 1: args = cls._indices(**kwargs) # Create the new Function object and invoke __init__ newcls = cls._symbol_type(name) options = kwargs.get('options', {}) newobj = sympy.Function.__new__(newcls, *args, **options) newobj.__init__(*args, **kwargs) # All objects cached on the AbstractFunction /newobj/ keep a reference # to /newobj/ through the /function/ field. Thus, all indexified # object will point to /newobj/, the "actual Function". newobj.function = newobj # Store new instance in symbol cache newcls._cache_put(newobj) return newobj
def _indices(cls, **kwargs): """Return the default dimension indices for a given data shape :param grid: :class:`Grid` that defines the spatial domain. :param dimensions: Optional, list of :class:`Dimension` objects that defines data layout. :return: Dimension indices used for each axis. ..note:: Only one of :param grid: or :param dimensions: is required. """ grid = kwargs.get('grid', None) dimensions = kwargs.get('dimensions', None) if grid is None: if dimensions is None: error("Creating a Function object requries either " "a 'grid' or the 'dimensions' argument.") raise ValueError("Unknown symbol dimensions or shape") else: if dimensions is not None: warning("Creating Function with 'grid' and 'dimensions' " "argument; ignoring the 'dimensions' and using 'grid'.") dimensions = grid.dimensions return dimensions
def _indices(cls, **kwargs): """Return the default dimension indices for a given data shape :param grid: :class:`Grid` object from which to infer the data shape and :class:`Dimension` indices. :return: Dimension indices used for each axis. """ save = kwargs.get('save', None) grid = kwargs.get('grid', None) time_dim = kwargs.get('time_dim', None) if grid is None: error('TimeFunction objects require a grid parameter.') raise ValueError('No grid provided for TimeFunction.') if time_dim is None: time_dim = grid.time_dim if save else grid.stepping_dim elif not isinstance(time_dim, TimeDimension): raise ValueError("time_dim must be a TimeDimension, not %s" % type(time_dim)) assert(isinstance(time_dim, Dimension) and time_dim.is_Time) _indices = Function._indices(**kwargs) return tuple([time_dim] + list(_indices))
def __init__(self, *args, **kwargs): if not self._cached(): self.nt = kwargs.get('nt', 0) self.npoint = kwargs.get('npoint') kwargs['shape'] = (self.nt, self.npoint) super(SparseFunction, self).__init__(self, *args, **kwargs) if self.grid is None: error('SparseFunction objects require a grid parameter.') raise ValueError('No grid provided for SparseFunction.') # Allocate and copy coordinate data d = Dimension('d') self.coordinates = Function(name='%s_coords' % self.name, dimensions=[self.indices[-1], d], shape=(self.npoint, self.grid.dim)) self._children.append(self.coordinates) coordinates = kwargs.get('coordinates', None) if coordinates is not None: self.coordinates.data[:] = coordinates[:]
def _test_f(): # FIXME: we get infinite recursion here: f = Function("f") assert residue(f(x)/x**5, x, 0) == f.diff(x, 4)/24
def test_var_cls(): f = var('f', cls=Function) assert isinstance(f, FunctionClass) g, h = var('g,h', cls=Function) assert isinstance(g, FunctionClass) assert isinstance(h, FunctionClass)
def test_function(): f = Function('f') l, x = map(Symbol, 'lx') assert exp(l(x))*l(x)/exp(l(x)) == l(x) assert exp(f(x))*f(x)/exp(f(x)) == f(x)
def test_sympy__physics__quantum__operator__DifferentialOperator(): from sympy.physics.quantum.operator import DifferentialOperator from sympy import Derivative, Function f = Function('f') assert _test_args(DifferentialOperator(1/x*Derivative(f(x), x), f(x)))
def test_apply_finite_diff(): x, h = symbols('x h') f = Function('f') assert (apply_finite_diff(1, [x-h, x+h], [f(x-h), f(x+h)], x) - (f(x+h)-f(x-h))/(2*h)).simplify() == 0 assert (apply_finite_diff(1, [5, 6, 7], [f(5), f(6), f(7)], 5) - (-S(3)/2*f(5) + 2*f(6) - S(1)/2*f(7))).simplify() == 0
def fct_sym_array(str_lst, coords=None): """ Construct list of symbols or functions with names in 'str_lst'. If 'coords' are given (tuple of symbols) function list constructed, otherwise a symbol list is constructed. """ if coords is None: fs_lst = [] for sym_str in str_lst: fs_lst.append(Symbol(sym_str)) else: fs_lst = [] for fct_str in str_lst: fs_lst.append(Function(fct_str)(*coords)) return fs_lst
def test_rsolve_raises(): x = Function('x') raises(ValueError, lambda: rsolve(y(n) - y(k + 1), y(n))) raises(ValueError, lambda: rsolve(y(n) - y(n + 1), x(n))) raises(ValueError, lambda: rsolve(y(n) - x(n + 1), y(n))) raises(ValueError, lambda: rsolve(y(n) - sqrt(n)*y(n + 1), y(n))) raises(ValueError, lambda: rsolve(y(n) - y(n + 1), y(n), {x(0): 0}))
def test_latex_printer(): r = Function('r')('t') assert VectorLatexPrinter().doprint(r ** 2) == "r^{2}"
def test_replace_map(): from sympy import symbols, Function, Matrix F, G = symbols('F, G', cls=Function) K = Matrix(2, 2, [(G(0), {F(0): G(0)}), (G(1), {F(1): G(1)}), (G(1), {F(1)\ : G(1)}), (G(2), {F(2): G(2)})]) M = Matrix(2, 2, lambda i, j: F(i+j)) N = M.replace(F, G, True) assert N == K
def python(expr, **settings): """Return Python interpretation of passed expression (can be passed to the exec() function without any modifications)""" printer = PythonPrinter(settings) exprp = printer.doprint(expr) result = '' # Returning found symbols and functions renamings = {} for symbolname in printer.symbols: newsymbolname = symbolname # Escape symbol names that are reserved python keywords if kw.iskeyword(newsymbolname): while True: newsymbolname += "_" if (newsymbolname not in printer.symbols and newsymbolname not in printer.functions): renamings[sympy.Symbol( symbolname)] = sympy.Symbol(newsymbolname) break result += newsymbolname + ' = Symbol(\'' + symbolname + '\')\n' for functionname in printer.functions: newfunctionname = functionname # Escape function names that are reserved python keywords if kw.iskeyword(newfunctionname): while True: newfunctionname += "_" if (newfunctionname not in printer.symbols and newfunctionname not in printer.functions): renamings[sympy.Function( functionname)] = sympy.Function(newfunctionname) break result += newfunctionname + ' = Function(\'' + functionname + '\')\n' if not len(renamings) == 0: exprp = expr.subs(renamings) result += 'e = ' + printer._str(exprp) return result
def _print_expint(self, e): from sympy import Function if e.args[0].is_Integer and self._use_unicode: return self._print_Function(Function('E_%s' % e.args[0])(e.args[1])) return self._print_Function(e)
def test_printmethod(): x = symbols('x') class nint(Function): def _fcode(self, printer): return "nint(%s)" % printer._print(self.args[0]) assert fcode(nint(x)) == " nint(x)"
def test_not_fortran(): x = symbols('x') g = Function('g') assert fcode( gamma(x)) == "C Not Fortran:\nC gamma(x)\n gamma(x)" assert fcode(Integral(sin(x))) == "C Not Fortran:\nC Integral(sin(x), x)\n Integral(sin(x), x)" assert fcode(g(x)) == "C Not Fortran:\nC g(x)\n g(x)"
def _functions(expr, x): """ Find the types of functions in expr, to estimate the complexity. """ from sympy import Function return set(e.func for e in expr.atoms(Function) if x in e.free_symbols)
def test_free_symbols(): from sympy import Function f = Function('f') assert mellin_transform(f(x), x, s).free_symbols == set([s]) assert mellin_transform(f(x)*a, x, s).free_symbols == set([s, a])
def test_sine_transform(): from sympy import sinh, cosh, EulerGamma t = symbols("t") w = symbols("w") a = symbols("a") f = Function("f") # Test unevaluated form assert sine_transform(f(t), t, w) == SineTransform(f(t), t, w) assert inverse_sine_transform( f(w), w, t) == InverseSineTransform(f(w), w, t) assert sine_transform(1/sqrt(t), t, w) == 1/sqrt(w) assert inverse_sine_transform(1/sqrt(w), w, t) == 1/sqrt(t) assert sine_transform( (1/sqrt(t))**3, t, w) == sqrt(w)*gamma(S(1)/4)/(2*gamma(S(5)/4)) assert sine_transform(t**(-a), t, w) == 2**( -a + S(1)/2)*w**(a - 1)*gamma(-a/2 + 1)/gamma((a + 1)/2) assert inverse_sine_transform(2**(-a + S( 1)/2)*w**(a - 1)*gamma(-a/2 + 1)/gamma(a/2 + S(1)/2), w, t) == t**(-a) assert sine_transform( exp(-a*t), t, w) == sqrt(2)*w/(sqrt(pi)*(a**2 + w**2)) assert inverse_sine_transform( sqrt(2)*w/(sqrt(pi)*(a**2 + w**2)), w, t) == exp(-a*t) assert sine_transform( log(t)/t, t, w) == -sqrt(2)*sqrt(pi)*(log(w**2) + 2*EulerGamma)/4 assert sine_transform( t*exp(-a*t**2), t, w) == sqrt(2)*w*exp(-w**2/(4*a))/(4*a**(S(3)/2)) assert inverse_sine_transform( sqrt(2)*w*exp(-w**2/(4*a))/(4*a**(S(3)/2)), w, t) == t*exp(-a*t**2)
def test_cosine_transform(): from sympy import sinh, cosh, Si, Ci t = symbols("t") w = symbols("w") a = symbols("a") f = Function("f") # Test unevaluated form assert cosine_transform(f(t), t, w) == CosineTransform(f(t), t, w) assert inverse_cosine_transform( f(w), w, t) == InverseCosineTransform(f(w), w, t) assert cosine_transform(1/sqrt(t), t, w) == 1/sqrt(w) assert inverse_cosine_transform(1/sqrt(w), w, t) == 1/sqrt(t) assert cosine_transform(1/( a**2 + t**2), t, w) == sqrt(2)*sqrt(pi)*exp(-a*w)/(2*a) assert cosine_transform(t**( -a), t, w) == 2**(-a + S(1)/2)*w**(a - 1)*gamma((-a + 1)/2)/gamma(a/2) assert inverse_cosine_transform(2**(-a + S( 1)/2)*w**(a - 1)*gamma(-a/2 + S(1)/2)/gamma(a/2), w, t) == t**(-a) assert cosine_transform( exp(-a*t), t, w) == sqrt(2)*a/(sqrt(pi)*(a**2 + w**2)) assert inverse_cosine_transform( sqrt(2)*a/(sqrt(pi)*(a**2 + w**2)), w, t) == exp(-a*t) assert cosine_transform(exp(-a*sqrt(t))*cos(a*sqrt( t)), t, w) == a*exp(-a**2/(2*w))/(2*w**(S(3)/2)) assert cosine_transform(1/(a + t), t, w) == sqrt(2)*( (-2*Si(a*w) + pi)*sin(a*w)/2 - cos(a*w)*Ci(a*w))/sqrt(pi) assert inverse_cosine_transform(sqrt(2)*meijerg(((S(1)/2, 0), ()), ( (S(1)/2, 0, 0), (S(1)/2,)), a**2*w**2/4)/(2*pi), w, t) == 1/(a + t) assert cosine_transform(1/sqrt(a**2 + t**2), t, w) == sqrt(2)*meijerg( ((S(1)/2,), ()), ((0, 0), (S(1)/2,)), a**2*w**2/4)/(2*sqrt(pi)) assert inverse_cosine_transform(sqrt(2)*meijerg(((S(1)/2,), ()), ((0, 0), (S(1)/2,)), a**2*w**2/4)/(2*sqrt(pi)), w, t) == 1/(t*sqrt(a**2/t**2 + 1))
def _print_Function(self, expr): """Print a Function object. :param expr: The expression. :rtype : str :return: The printed string. :raise RuntimeError: Raise if the function is not supported. """ assert isinstance(expr, _sympy.Function) # Check the function. fn_object = _mexp_function.find_sympy_function(expr.func.__name__) if fn_object is None: raise RuntimeError("Unsupported function: \"%s\"." % expr.func.__name__) if fn_object.get_argument_count() != len(expr.args): raise RuntimeError("Argument count mismatch.") # Stringify the arguments. arg_text = "" for arg_id in range(0, len(expr.args)): arg_text += self.doprint(expr.args[arg_id]) if arg_id + 1 != len(expr.args): arg_text += "," return "%s(%s)" % (fn_object.get_function_name(), arg_text)
def _print_Function(self, e): """Print a Function object. :param e: The expression. :rtype : bce.dom.mathml.all.Base :return: The printed MathML object. """ assert isinstance(e, _sympy.Function) # Check the function. fn_object = _mexp_function.find_sympy_function(e.func.__name__) if fn_object is None: raise RuntimeError("Unsupported function: \"%s\"." % e.func.__name__) if fn_object.get_argument_count() != len(e.args): raise RuntimeError("Argument count mismatch.") # Build the node. node = _mathml.RowComponent() node.append_object(_mathml.TextComponent(fn_object.get_function_name())) node.append_object(_mathml.OperatorComponent(_mathml.OPERATOR_LEFT_PARENTHESIS)) for arg_id in range(0, len(e.args)): arg_value = e.args[arg_id] node.append_object(self.doprint(arg_value)) if arg_id + 1 != len(e.args): node.append_object(_mathml.OperatorComponent(_mathml.OPERATOR_SEPARATOR)) node.append_object(_mathml.OperatorComponent(_mathml.OPERATOR_RIGHT_PARENTHESIS)) return node
def test_printmethod(): class nint(Function): def _fcode(self, printer): return "nint(%s)" % printer._print(self.args[0]) assert fcode(nint(x)) == "nint(x)"
def test_user_functions(): g = Function('g') assert fcode(g(x), user_functions={"g": "great"}) == "great(x)" assert fcode(sin(x), user_functions={"sin": "zsin"}) == "zsin(x)" assert fcode(gamma(x), user_functions={"gamma": "mygamma"}) == "mygamma(x)" assert fcode(factorial(n), user_functions={"factorial": "fct"}) == "fct(n)"
def generate_helpers_C(self, chunk_size=100): """ translates the helpers to C code using SymEngine’s `C-code printer <https://github.com/symengine/symengine/pull/1054>`_. Parameters ---------- chunk_size : integer If the number of instructions in the final C code exceeds this number, it will be split into chunks of this size. See `large_systems` on why this is useful. If there is an obvious grouping of your helpers, the group size suggests itself for `chunk_size`. If smaller than 1, no chunking will happen. """ if self.helpers: get_helper = symengine.Function("get_general_helper") set_helper = symengine.Function("set_general_helper") for i,helper in enumerate(self.helpers): self.general_subs[helper[0]] = get_helper(i) self.render_and_write_code( (set_helper(i, helper[1].subs(self.general_subs)) for i,helper in enumerate(self.helpers)), name = "general_helpers", chunk_size = chunk_size, arguments = self._default_arguments() + [("general_helper","double *__restrict const")], omp = False, ) self._helper_C_source = True
def test_f(): f = Function("f") assert residue(f(x)/x**5, x, 0) == f(x).diff(x, 4).subs(x, 0)/24
def _atomic(e): """Return atom-like quantities as far as substitution is concerned: Derivatives, Functions and Symbols. Don't return any 'atoms' that are inside such quantities unless they also appear outside, too. Examples ======== >>> from sympy import Derivative, Function, cos >>> from sympy.abc import x, y >>> from sympy.core.basic import _atomic >>> f = Function('f') >>> _atomic(x + y) {x, y} >>> _atomic(x + f(y)) {x, f(y)} >>> _atomic(Derivative(f(x), x) + cos(x) + y) {y, cos(x), Derivative(f(x), x)} """ from sympy import Derivative, Function, Symbol pot = preorder_traversal(e) seen = set() try: free = e.free_symbols except AttributeError: return {e} atoms = set() for p in pot: if p in seen: pot.skip() continue seen.add(p) if isinstance(p, Symbol) and p in free: atoms.add(p) elif isinstance(p, (Derivative, Function)): pot.skip() atoms.add(p) return atoms