Search code examples
pythonsympy

Define an unused new symbol inside function scope


I find difficult to really encapsulate a lot of logic in a reusable manner when writing sympy code

I would like to write a function in the following way:

def compute_integral(fn):
  s = symbols_new() # do not apply lexicographic identity, this symbol identity is unique
  return integrate( fn(s), (s, 0, 1))

If I just use the usual symbols() to instantiate s, I risk fn having the same symbol inside its defining expression somewhere, for instance the following can occur:

def compute_integral(fn):
  s = symbols("s")
  return integrate(fn(s), (s, 0, 1))

...

import sympy as sp
from sympy.abc import *

t = sp.sin(3 + s)
f = sp.lambdify([x], x**2 + t * s * x)
compute_integral(f) #< ---- symbol s is already present in the function, now the integral will produce the wrong result

What I'm doing right now to minimize this issue is less than ideal:

compute_integral_local_var = 0

def compute_integral(fn):
  global compute_integral_local_var
  compute_integral_local_var += 1
  s = symbols("local_{}".format(compute_integral_local_var))
  return integrate(fn(s), (s, 0, 1))

I want to avoid these kind of collisions without having to add a special global variable. Any better approaches to achieve this type of encapsulation within the sympy API?


Solution

  • I'm using the latest sympy, version 1.13.2, and the code block you provided doesn't run: it raises an error.

    There seems to be some misconceptions that I'd like to clarify. Consider this code block:

    import sympy as sp
    from sympy.abc import s, x
    
    t = sp.sin(3 + s)
    print("t:", t)
    e = x**2 + t * s * x
    print("e:", e)
    # t: sin(s + 3)
    # e: s*x*sin(s + 3) + x**2
    

    Here, I create two symbolic expressions, t and e. t contains a symbolic function, sin, and e is composed of t. You can consider e as a function, but in sympy terminology, e is a symbolic expression.

    Next, in your example you executed something like this:

    f = sp.lambdify([x], e)
    

    which creates a numerical function which will be evaluated by NumPy. Then, you attempted to integrate symbolically this numerical function, which is absolutely wrong. With SymPy, you can only perform symbolic integrations of symbolic expressions.

    If all you are trying to perform is a symbolic integration, then this command is sufficient:

    sp.integrate(e, (x, 0, 1))
    # s*sin(s + 3)/2 + 1/3
    

    If you'd like to integrate multiple symbolic expressions over the same range, then you can create a custom function:

    def compute_integral(expr, sym):
        return sp.integrate(expr, (sym, 0, 1))
    
    print(compute_integral(e, x))
    # s*sin(s + 3)/2 + 1/3
    
    print(compute_integral(t, x))
    # sin(s + 3)
    

    EDIT:

    Based on the little details you shared, python's lambda function and sympy's Dummy symbols might get what you want. A Dummy symbol is guaranteed to be unique: it is used when a temporary symbol is needed or when a name of a symbol is not important.

    def compute_integral(func):
        s = sp.Dummy()
        return sp.integrate(func(s), (s, 0, 1))
    
    t = sp.sin(3 + s)
    f = lambda x: x**2 + t * s * x
    compute_integral(f)
    # s*sin(s + 3)/2 + 1/3
    

    Please, please, please: don't use functions created with lambdify as arguments of sympy functions: it generally doesn't work. You provided the following example:

    from sympy.abc import r, s
    sp.lambdify([r], r**2)(s).diff(s)
    # out: 2*s
    

    You were lucky, the numerical function generated by lambdify only contains plain python operations (a multiplication, in this cae). Should the original symbolic expression contain other functions, like sin, cos, exp, etc..., you would get an error:

    sp.lambdify([r], sin(r))(s).diff(s)
    
    ---------------------------------------------------------------------------
    AttributeError                            Traceback (most recent call last)
    AttributeError: 'Symbol' object has no attribute 'sin'
    
    The above exception was the direct cause of the following exception:
    
    TypeError                                 Traceback (most recent call last)
    Cell In[50], line 1
    ----> 1 sp.lambdify([r], sin(r))(s).diff(s)
    
    File <lambdifygenerated-4>:2, in _lambdifygenerated(r)
          1 def _lambdifygenerated(r):
    ----> 2     return sin(r)
    
    TypeError: loop of ufunc does not support argument 0 of type Symbol which has no callable sin method