Home > Mobile >  How to add a callable function to a Sympy expression
How to add a callable function to a Sympy expression

Time:06-01

If I have an expression x = Symbol('x') and f1=x**2 and I want to further add some f2 where f2 = interp1d(t, y) is scipy interpolation. How does one turn f2 into an expression such that I have somthing like f = x**2 f2(x) so that I can later evaluate f as f.subs(x, some_number)?

Due to specification of the code, I can't evaluate f1 and f2 separatelly and then add the resulting numbers, I need to be able to add it to an existing sympy expression and evaluate it using something like .subs()

CodePudding user response:

One way but it requires hard-coding the function to be called in the class:

f2 = lambda t: np.sin(t)

class MyFunc(Function):
    @classmethod
    def eval(cls, arg):
        arg = sympify(arg, strict=True)
        if arg.is_Number:
            return sympify(f2(float(arg)), strict=True)

More like Davide's answer but with a couple of fixes:

class FuncWrapper(Symbol):
    """Wraps a python callable as a Basic instance"""
    def __new__(cls, func, name):
        obj = super().__new__(cls, name)
        obj._wrapped = func
        return obj

    @property
    def wrapped(self):
        return self._wrapped

    def _hashable_content(self):
        return (self.wrapped,) # needed for __eq__

    def eval(self, arg):
        if arg.is_Number:
            return sympify(self.wrapped(float(arg)))

    def __call__(self, arg):
        return Call(self, arg)


class Call(Function):
    @classmethod
    def eval(cls, func, arg):
        arg = sympify(arg)

        result = func.eval(arg)
        if result is not None:
            return result

With that you have:

In [61]: f = FuncWrapper(np.sin, 'f')

In [62]: x   f(x)
Out[62]: x   Call(f, x)

In [63]: _.subs(x, 1)
Out[63]: 1.84147098480790

CodePudding user response:

One very risky way would be to create a wrapper object for your numerical function, like this:

from sympy import *
import numpy as np
var("x")

# symbolic expression
f1 = cos(x)
# numerical function
f2 = lambda t: np.sin(t)

class MyFunc(Expr):
    """Create a symbolic wrapper to a numerical function."""
    
    def __new__(cls, arg, **kwargs):
        obj = Expr.__new__(cls, **kwargs)
        obj._custom_func = arg
        return obj
    
    def _subs(self, old, new, **hints):
        return self._custom_func(float(new))

expr = f1   MyFunc(f2)
expr.subs(x, np.pi/4)
# out: 1.41421356237309
  • Related