0

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)
SergGr
  • 23,570
  • 2
  • 30
  • 51
  • I don't think those `eval()` calls are doing what you think they are doing. Try them in the REPL. – Kevin Feb 09 '18 at 20:19
  • 1
    There is no [mcve] here. We don't know what was entered in response to all those input prompts. –  Feb 09 '18 at 20:23
  • @Victor Reaver you might be interested in ongoing work which would allow SymPy to generate code for Newton's method directly from a symbolic expression: https://github.com/sympy/sympy/pull/13100/files#diff-3a6d66b018fe657a69245850eb8f2a1aR11 – Bjoern Dahlgren Feb 10 '18 at 09:47

1 Answers1

1

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.

SergGr
  • 23,570
  • 2
  • 30
  • 51
  • It says name 'x' is not defined. I am not sure why. It throws that error after I enter the function for f_inp. I would be grateful for a hint. Thank you for fixing the initial code though. – Victor Reaver Feb 10 '18 at 09:25
  • Never mind actually, I fixed it. Had to swap places between x=sympy.symbols('x') and f_inp and add an eval for the f_inp. Once again, thank you so much for your help! – Victor Reaver Feb 10 '18 at 09:27
  • @VictorReaver, sorry. I missed that you used Python 3.x and so `input` doesn't do `eval` automatically. I edited my answer to present both 2.x and 3.x versions. – SergGr Feb 12 '18 at 18:36