Q: Lambdify a class of the form def eval(x, y, z, *args)

29 views
Skip to first unread message

Audrius-St

unread,
Sep 6, 2021, 8:30:23 PM9/6/21
to sympy
Hello,

Is it possible to lambdify the following:

# test code
mu, m_t, g = sp.symbols('mu, m_t, g')

class h_Kepler_two_body_polar(sp.Function):
    nargs = (1, 2, 3, (4, 5, 6))  # Is this correct? 

    @classmethod
    def eval(cls, r, p_r, p_phi, *args):
        mu = args[0]
        m_t = args[1]
        g = args[2]

        h = p_r**2/(2*mu) + p_phi**2/(2*mu*r**2) - g*mu*m_t/r
        return h

My current attempt to lambdify 

    H_Ktb_polar_lf = sp.lambdify(
                [r, p_r, p_phi, (mu, m_t, g)],
                h_Kepler_two_body_polar(r, p_r, p_phi, (mu, m_t, g)), 'numpy')

returns the error message 

TypeError: h_Kepler_two_body_polar takes at least 1 argument (4 given)

The reason for using *args is that later in the code I use fsolve from SciPy with

func: callable f(x, *args) and  fprime: callable f_prime(x, *args)

Any insight would be appreciated.

Nicolas Guarin

unread,
Sep 7, 2021, 11:52:39 AM9/7/21
to sympy
Do you have a Minimal Working Example (MWE)?

Aaron Meurer

unread,
Sep 7, 2021, 2:12:16 PM9/7/21
to sympy
You seem to be quite a bit confused here.

First off, you are right that you aren't using nargs correctly. nargs
lists the number of arguments a function can take, not its signature.

However, you don't need to define a Function subclass at all. Function
subclasses let you define functions that are unevaluated, which
happens when eval() returns None. But if you never return None from
eval(), there's no point to having a Function subclass. That's the
same as just a normal Python function. Or, more simply, you can just
define an expression, like

mu, m_t, g, r, p_phi, p_r = sp.symbols('mu, m_t, g, r, p_phi, p_r')
h_Kepler_two_body_polar = p_r**2/(2*mu) + p_phi**2/(2*mu*r**2) - g*mu*m_t/r
H_Ktb_polar_lf = sp.lambdify(
[r, p_r, p_phi, (mu, m_t, g)],
h_Kepler_two_body_polar, 'numpy')

It's not necessary to make h_Kepler_two_body_polar into a function
unless you need this level of indirection. But note that you can
always replace symbols with numbers in an expression using subs(), so
strictly speaking this level of indirection should never be needed.

I'm not sure what you had r, p_phi, and p_r defined as, but they need
to be defined as symbols to use them as arguments to lambdify.
lambdify takes a symbolic expression as input and turns it into a
function based on the symbols in the expression (which are specified
by the first argument to lambdify()).

Aaron Meurer
> --
> You received this message because you are subscribed to the Google Groups "sympy" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sympy+un...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/98a6147c-a081-4432-8168-d0d6b5139247n%40googlegroups.com.

Audrius-St

unread,
Sep 7, 2021, 4:08:26 PM9/7/21
to sympy
On Tuesday, September 7, 2021 at 2:12:16 PM UTC-4 asme...@gmail.com wrote:
You seem to be quite a bit confused here.

Yes, I may be.

First off, you are right that you aren't using nargs correctly. nargs
lists the number of arguments a function can take, not its signature.

Understood.  

However, you don't need to define a Function subclass at all. Function
subclasses let you define functions that are unevaluated, which
happens when eval() returns None. But if you never return None from
eval(), there's no point to having a Function subclass. That's the
same as just a normal Python function. Or, more simply, you can just
define an expression, like

mu, m_t, g, r, p_phi, p_r = sp.symbols('mu, m_t, g, r, p_phi, p_r')
h_Kepler_two_body_polar = p_r**2/(2*mu) + p_phi**2/(2*mu*r**2) - g*mu*m_t/r
H_Ktb_polar_lf = sp.lambdify(
[r, p_r, p_phi, (mu, m_t, g)],
h_Kepler_two_body_polar, 'numpy')

It's not necessary to make h_Kepler_two_body_polar into a function
unless you need this level of indirection. 

Understood. 
Old OO habits.

Now 

mu, m_t, g = sp.symbols('mu, m_t, g')

def h_Kepler_two_body_polar(r, p_r, p_phi, *args):
    mu = args[0]
    m_t = args[1]
    g = args[2]

    h = p_r**2/(2*mu) + p_phi**2/(2*mu*r**2) - g*mu*m_t/r
    return h 

. . .

    # Lambdify the symbolic Hamiltonian to numeric
    H_Ktb_polar_lf = sp.lambdify(
                [r, p_r, p_phi, (mu, m_t, g)],
                h_Kepler_two_body_polar(r, p_r, p_phi, mu, m_t, g), 'numpy')

. . .

    # Energy - Hamiltonian first integral
    Ho = H_Ktb_polar_lf(R_o, Pr_o, Phi_o, (Mu, M_t, G))

works.

Was unfamiliar with the use of *args.


I'm not sure what you had r, p_phi, and p_r defined as, but they need
to be defined as symbols to use them as arguments to lambdify.
lambdify takes a symbolic expression as input and turns it into a
function based on the symbols in the expression (which are specified
by the first argument to lambdify()).

Defined, not shown for brevity. 

Thanks for your reply and explanations.
Reply all
Reply to author
Forward
0 new messages