Search code examples
python-3.xmathsympynewtons-methodlambdify

Why does the lambdify function from sympy throw an error in my case?


The error lies with the lambdify function. It says syntax error for some reason. This is what is says exactly:

File "D:\Anaconda\lib\site-packages\sympy\utilities\lambdify.py", line 434, in lambdify func = eval(lstr, namespace).

The program crashes right after the user enters the tolerance. This is a Newtons Method program (or at least my attempt at one). Any suggestions on how to improve this code in general would be greatly appreciated too.

What I enter for f and dfdx is the following:

x**3 + 3*x + 5 

and

3*x**2 + 3 

respectively. As for v0 and eps, I enter 1 and 0.000001 respectively. The program terminates with the aforementioned error no matter what I enter (assuming it uses x as the variable, otherwise it says undefined and that's it).

import sympy
import sys

v0 = int(input("Please enter the v0 value: "))

eps = float(input("Please enter the tolerance: "))

f = lambda x : eval('input("Enter the function: ")')

dfdx = lambda x : eval('input("Enter the derivative: ")')

x=sympy.symbols('x')

func = sympy.lambdify(f,x)

deriv = sympy.lambdify(f,x)

def Newton(func, deriv, v0, eps):
    f_value = func(v0)
    iteration_counter = 0
    while abs(f_value) > eps and iteration_counter < 100:
        try:
            v0 = v0 - float(f_value)/deriv(v0)
        except ZeroDivisionError:
            print ("Error! - derivative zero for x = ", v0)
            sys.exit(1)     # Abort with error

        f_value = func(v0)
        iteration_counter += 1

    # Here, either a solution is found, or too many iterations
    if abs(f_value) > eps:
        iteration_counter = -1
    return v0, iteration_counter

Newton(func,deriv,v0,eps)

Solution

  • AFAIU your cunning plan is to first use Python interpreter to build a function from user's string (using input) and then to use SymPy to build a re-usable function from that. There are a few problems with your code, the most important probably is that you get signature of SymPy methods wrong.

    Also idea of providing dfdx as input doesn't look good to me. If the original function f has a good derivative that you can calculate, SymPy can probably calculate it on its own.

    So if you fix some obvious mistakes, you may get a code like this:

    Python 3.x

    import sympy
    import sys
    
    v0 = int(input("Please enter the v0 value: "))
    eps = float(input("Please enter the tolerance: "))
    f_inp = lambda x: eval(input("Enter the function: "))
    x = sympy.symbols('x')
    f_symb = f_inp(x)
    func = sympy.lambdify(x, f_symb)
    deriv = sympy.lambdify(x, sympy.diff(f_symb, x))
    
    def Newton(func, deriv, v0, eps):
        f_value = func(v0)
        iteration_counter = 0
        while abs(f_value) > eps and iteration_counter < 100:
            try:
                v0 = v0 - float(f_value) / deriv(v0)
            except ZeroDivisionError:
                print ("Error! - derivative zero for x = ", v0)
                sys.exit(1)  # Abort with error
    
            f_value = func(v0)
            iteration_counter += 1
    
        # Here, either a solution is found, or too many iterations
        if abs(f_value) > eps:
            iteration_counter = -1
        return v0, iteration_counter
    
    print (Newton(func, deriv, v0, eps))
    

    Python 2.x

    import sympy
    import sys
    
    v0 = int(input("Please enter the v0 value: "))
    eps = float(input("Please enter the tolerance: "))
    f_inp = lambda x: input("Enter the function: ")
    x = sympy.symbols('x')
    f_symb = f_inp(x)
    func = sympy.lambdify(x, f_symb)
    deriv = sympy.lambdify(x, sympy.diff(f_symb, x))
    
    def Newton(func, deriv, v0, eps):
        f_value = func(v0)
        iteration_counter = 0
        while abs(f_value) > eps and iteration_counter < 100:
            try:
                v0 = v0 - float(f_value) / deriv(v0)
            except ZeroDivisionError:
                print ("Error! - derivative zero for x = ", v0)
                sys.exit(1)  # Abort with error
    
            f_value = func(v0)
            iteration_counter += 1
    
        # Here, either a solution is found, or too many iterations
        if abs(f_value) > eps:
            iteration_counter = -1
        return v0, iteration_counter
    
    print Newton(func, deriv, v0, eps)
    

    which for your input

    1
    0.000001
    x**3 + 3*x + 5 
    

    produces following output:

    (-1.154171557329764, 5)

    The main difference between 2.x and 3.x versions is that input in 2.x calls eval inside while input in 3.x doesn't.