How to Make Python Print something at nth Iteration within a For Loop?

780 Views Asked by At

I want to preface my question by saying that I am very new to Python and have only started using it for a specific class in grad school.

I have written a script to find the root of a function using iterative methods such as the Fixed-point, Bisection, and Newton-Raphson methods. The python code for my algorithms are as follows:

Fixed-Point Method:

def fixedpoint(func, g_func,x0,tol, MAXIT):
    
    def g(x):
        return eval(g_func)
    
    print(f"Seeking root of {func}.")
    print("FIXED POINT ITERATION:")
    iterated_x = g(x0)
    for i in range(0, MAXIT):
        iterated_x = g(iterated_x)
        print("Iteration", i + 1, iterated_x)
        condition = abs(g(iterated_x) - (iterated_x)) 
        if condition < tol:
            print(f"The root converges to {iterated_x} after {i+1} iterations.")
            break
        if i == MAXIT and condition >= tol:
            print("ERROR: The root did not converge after maximum iterations.")

fixedpoint('x**2 - 4*x + 2', '(x**2+2)/4', 0.75, 10**(-4), 100)
fixedpoint('math.exp(-x) + x - 7 ', '7 - math.exp(-x)', 0.75, 10**(-4), 100)

Bisection Method:

def bisection(func,a, b, tol, MAXIT):
    
    def f(x):
        return eval(func)
    
    print(f"Seeking root of {func}")
    print("BISECTION METHOD:")
    if f(a)*f(b) <0:
        for i in range(0, MAXIT):
            c = (a+b)/2
            if f(a)*f(c) < 0:
                a = a
                b = c
                print(f"Iteration", i + 1, f(c))
            elif f(b)*f(c) < 0:
                b = b
                a = c
                print(f"Iteration", i + 1, f(c))
            if f(c) == 0 or abs(f(c)) < tol:
                print ("Exact Solution Found")
                print(f"Iteration", i + 1, c)
                print("The root converges to", c, "after", i + 1, "iterations.")
                break
            elif i == MAXIT and abs(f(c)) > tol:
                print("Bisection method fails.")
                
    return None

bisection('x**2 - 4*x + 2',0.5, 1, 10**(-4), 100)
bisection('math.exp(-x) + x - 7',6, 8, 10**(-4), 100)

Newton-Raphson Method:

def newtonraphson(func, deriv, x0, tol, MAXIT):
    
    def f(x):
        return eval(func)

    def ddx(x):
        return eval(deriv)
        
    
    print(f"Seeking root of {func}.")
    print("NEWTON-RAPHSON METHOD:")
    
    for i in range(1, MAXIT):
        iterated_x = x0 - (f(x0)/ddx(x0))
        x0 = iterated_x
        print(f"Iteration", i, x0)

        if f(x0) < tol:
            print(f"The root converges to {x0} after {i} iterations.")
            break
        elif i==MAXIT and f(x0) > tol:
            print("After maximum iterations, root was not found.")

newtonraphson('x**2-4*x+2', '2*x-4', 0.75,10**(-4), 100)
newtonraphson('math.exp(-x) + x - 7', '-(math.exp(-x)) + 1', 0.75,10**(-4), 100)

While my script is successfully able to find the roots for the equations I am interested in, I am stuck with a simpler problem. Basically, I want to tell my program that if after maximum iterations, my tolerance condition is not fulfilled, print "Method Failed".

However, my code does not print the statement I want it to when I experiment with functions that don't coverage when maximum iterations is set at 100 and tolerance is set to 0.0001.

Is my syntax right for the fail print statement?

Do conditions like "if i==MAXIT and f(x0) > tol" make sense in the context of the function I have written?

I would appreciate any and all advice for this issue as I am trying hard to improve at Python.

Thank you.

2

There are 2 best solutions below

1
Drew Hall On BEST ANSWER

A good way to achieve this in Python is to use the for\else construct. Essentially, if you hang an "else" clause off of your for loop, it will execute if the loop doesn't terminate early due to a break statement. It's very useful for loops that are searching for something, and root finding definitely fits that mold. So your fixed point iteration loop would look like:

for i in range(0, MAXIT):
    iterated_x = g(iterated_x)
    condition = abs(g(iterated_x) - (iterated_x)) 
    if condition < tol:
        print(f"The root converges to {iterated_x} after {i+1} iterations.")
        break
else:
    print("ERROR: The root did not converge after maximum iterations.")

One advantage of doing it this way is that it doesn't matter if you change your success criteria in the loop--the else clause runs if you don't declare success with the break statement no matter how success is defined.

1
user3124634 On

The problem you are facing is that range in python only function from the start index to one before the end index. That is, range(0,3) goes over the numbers (0,1,2). What is happening in your code is that you simply fail to activate the fail condition because the index never actually reaches that value. However, the fix is easy. In your functions where you say:

for i in range(1,MAXIT):
    if f(x0) < tol:
        print(f"The root converges to {x0} after {i} iterations.")
        break
    elif i==MAXIT and f(x0) > tol:
        print("After maximum iterations, root was not found.")

simply replace it with

for i in range(1,MAXIT):
    if f(x0) < tol:
        print(f"The root converges to {x0} after {i} iterations.")
        return

print("After maximum iterations, root was not found.")

instead. What this will do is stop the function if it finds the solution, and give you your error message if you manage to get through the entire loop without finding the answer.