Python: Finding multiple roots of nonlinear equation - python

Assume the following function:
f(x) = x * cos(x-4)
With x = [-2.5, 2.5] this function crosses 0 at f(0) = 0 and f(-0.71238898) = 0.
This was determined with the following code:
import math
from scipy.optimize import fsolve
def func(x):
return x*math.cos(x-4)
x0 = fsolve(func, 0.0)
# returns [0.]
x0 = fsolve(func, -0.75)
# returns [-0.71238898]
What is the proper way to use fzero (or any other Python root finder) to find both roots in one call? Is there a different scipy function that does this?
fzero reference

I once wrote a module for this task. It's based on chapter 4.3 from the book Numerical Methods in Engineering with Python by Jaan Kiusalaas:
import math
def rootsearch(f,a,b,dx):
x1 = a; f1 = f(a)
x2 = a + dx; f2 = f(x2)
while f1*f2 > 0.0:
if x1 >= b:
return None,None
x1 = x2; f1 = f2
x2 = x1 + dx; f2 = f(x2)
return x1,x2
def bisect(f,x1,x2,switch=0,epsilon=1.0e-9):
f1 = f(x1)
if f1 == 0.0:
return x1
f2 = f(x2)
if f2 == 0.0:
return x2
if f1*f2 > 0.0:
print('Root is not bracketed')
return None
n = int(math.ceil(math.log(abs(x2 - x1)/epsilon)/math.log(2.0)))
for i in range(n):
x3 = 0.5*(x1 + x2); f3 = f(x3)
if (switch == 1) and (abs(f3) >abs(f1)) and (abs(f3) > abs(f2)):
return None
if f3 == 0.0:
return x3
if f2*f3 < 0.0:
x1 = x3
f1 = f3
else:
x2 =x3
f2 = f3
return (x1 + x2)/2.0
def roots(f, a, b, eps=1e-6):
print ('The roots on the interval [%f, %f] are:' % (a,b))
while 1:
x1,x2 = rootsearch(f,a,b,eps)
if x1 != None:
a = x2
root = bisect(f,x1,x2,1)
if root != None:
pass
print (round(root,-int(math.log(eps, 10))))
else:
print ('\nDone')
break
f=lambda x:x*math.cos(x-4)
roots(f, -3, 3)
roots finds all roots of f in the interval [a, b].

Define your function so that it can take either a scalar or a numpy array as an argument:
>>> import numpy as np
>>> f = lambda x : x * np.cos(x-4)
Then pass a vector of arguments to fsolve.
>>> x = np.array([0.0, -0.75])
>>> fsolve(f,x)
array([ 0. , -0.71238898])

In general (i.e. unless your function belongs to some specific class) you can't find all the global solutions - these methods usually do local optimization from given starting points.
However, you can switch math.cos() with numpy.cos() and that will vectorize your function so it can solve for many values at once, e.g. fsolve(func, np.arange(-10,10,0.5)).

Related

Find more than one root for a nonlinear system of equations - Python [duplicate]

Assume the following function:
f(x) = x * cos(x-4)
With x = [-2.5, 2.5] this function crosses 0 at f(0) = 0 and f(-0.71238898) = 0.
This was determined with the following code:
import math
from scipy.optimize import fsolve
def func(x):
return x*math.cos(x-4)
x0 = fsolve(func, 0.0)
# returns [0.]
x0 = fsolve(func, -0.75)
# returns [-0.71238898]
What is the proper way to use fzero (or any other Python root finder) to find both roots in one call? Is there a different scipy function that does this?
fzero reference
I once wrote a module for this task. It's based on chapter 4.3 from the book Numerical Methods in Engineering with Python by Jaan Kiusalaas:
import math
def rootsearch(f,a,b,dx):
x1 = a; f1 = f(a)
x2 = a + dx; f2 = f(x2)
while f1*f2 > 0.0:
if x1 >= b:
return None,None
x1 = x2; f1 = f2
x2 = x1 + dx; f2 = f(x2)
return x1,x2
def bisect(f,x1,x2,switch=0,epsilon=1.0e-9):
f1 = f(x1)
if f1 == 0.0:
return x1
f2 = f(x2)
if f2 == 0.0:
return x2
if f1*f2 > 0.0:
print('Root is not bracketed')
return None
n = int(math.ceil(math.log(abs(x2 - x1)/epsilon)/math.log(2.0)))
for i in range(n):
x3 = 0.5*(x1 + x2); f3 = f(x3)
if (switch == 1) and (abs(f3) >abs(f1)) and (abs(f3) > abs(f2)):
return None
if f3 == 0.0:
return x3
if f2*f3 < 0.0:
x1 = x3
f1 = f3
else:
x2 =x3
f2 = f3
return (x1 + x2)/2.0
def roots(f, a, b, eps=1e-6):
print ('The roots on the interval [%f, %f] are:' % (a,b))
while 1:
x1,x2 = rootsearch(f,a,b,eps)
if x1 != None:
a = x2
root = bisect(f,x1,x2,1)
if root != None:
pass
print (round(root,-int(math.log(eps, 10))))
else:
print ('\nDone')
break
f=lambda x:x*math.cos(x-4)
roots(f, -3, 3)
roots finds all roots of f in the interval [a, b].
Define your function so that it can take either a scalar or a numpy array as an argument:
>>> import numpy as np
>>> f = lambda x : x * np.cos(x-4)
Then pass a vector of arguments to fsolve.
>>> x = np.array([0.0, -0.75])
>>> fsolve(f,x)
array([ 0. , -0.71238898])
In general (i.e. unless your function belongs to some specific class) you can't find all the global solutions - these methods usually do local optimization from given starting points.
However, you can switch math.cos() with numpy.cos() and that will vectorize your function so it can solve for many values at once, e.g. fsolve(func, np.arange(-10,10,0.5)).

Secant method using python

How would you write python code to find the root of the non linear equation fnon(x) = 0 using the secant method? Using secant(fnon,x0,x1,tol) returning x as the computed root and f as the function value at that root. My method isn't working
A solution provided by the website "Solving nonlinear algebraic equations" which has additional ways to calculate it.
def secant(f, x0, x1, eps):
f_x0 = f(x0)
f_x1 = f(x1)
iteration_counter = 0
while abs(f_x1) > eps and iteration_counter < 100:
try:
denominator = float(f_x1 - f_x0)/(x1 - x0)
x = x1 - float(f_x1)/denominator
except ZeroDivisionError:
print "Error! - denominator zero for x = ", x
sys.exit(1) # Abort with error
x0 = x1
x1 = x
f_x0 = f_x1
f_x1 = f(x1)
iteration_counter += 1
# Here, either a solution is found, or too many iterations
if abs(f_x1) > eps:
iteration_counter = -1
return x, iteration_counter
def f(x):
return x**2 - 9
x0 = 1000; x1 = x0 - 1
solution, no_iterations = secant(f, x0, x1, eps=1.0e-6)
if no_iterations > 0: # Solution found
print "Number of function calls: %d" % (2 + no_iterations)
print "A solution is: %f" % (solution)
else:
print "Solution not found!"
#example function
import numpy as np
fnon= lambda x: np.sin(x-np.pi)
from scipy.optimize import root_scalar
x0=0
x1=5
tol=1e-8
x= root_scalar(fnon,method='secant',x0=x0,x1=x1,rtol=tol)
print(x)

Roots of a quadratic function: math domain error [duplicate]

This question already has an answer here:
ValueError: math domain error - Quadratic Equation (Python)
(1 answer)
Closed 4 years ago.
I want to define a function that returns the values of the roots. It is supposed to return always something.
If b**2 - 4ac < 0, then it is supposed to return [ ], but it appears as an error.
My code is this by now:
from math import*
def solve(a, b, c):
x = sqrt(b**2 - 4*a*c)
if x > 0:
x1 = (-b + x)/(2*a)
x2 = (-b - x)/(2*a)
return [x1, x2]
elif x == 0:
x1 = x2 = -b/(2*a)
return [x1]
else:
return []
The math.sqrt is undefined for negative numbers and thus returns a ValueError.
If you wish to return the complex square root for negatives, use x**0.5:
x = (b**2 - 4*a*c)**0.5
Alternatively use the cmath.sqrt implementation:
from cmath import sqrt
x = sqrt(b**2 - 4*a*c)
sqrt will not accept negative values. To avoid this, you can check your 'else' condition before computing the square root:
from math import sqrt
def solve(a, b, c):
formula = b**2 - 4*a*c
if formula < 0:
return []
x = sqrt(formula)
if x > 0:
x1 = (-b + x)/(2*a)
x2 = (-b - x)/(2*a)
return [x1, x2]
elif x == 0:
x1 = x2 = -b/(2*a)
return [x1]

Computing integral with the Trapezoidal Rule (the approximate value of the integral, and the number of iterations)

The program needs to compute define integral with a predetermined
accuracy (eps) with the Trapezoidal Rule and my function needs to return:
1.the approximate value of the integral.
2.the number of iterations.
My code:
from math import *
def f1(x):
return (x ** 2 - 1)**(-0.5)
def f2(x):
return (cos(x)/(x + 1))
def integral(f,a,b,eps):
n = 2
x = a
h = (b - a) / n
sum = 0.5 * (f(a) + f(b))
for i in range(n):
sum = sum + f(a + i * h)
sum_2 = h * sum
k = 0
flag = 1
while flag == 1:
n = n * 2
sum = 0
k = k + 1
x = a
h = (b - a) / n
sum = 0.5 * (f(a) + f(b))
for i in range(n):
sum = sum + f(a + i * h)
sum_new = h * sum
if eps > abs(sum_new - sum_2):
t1 = sum_new
t2 = k
return t1, t2
else:
sum_2 = sum_new
x1 = float(input("First-begin: "))
x2 = float(input("First-end: "))
y1 = float(input("Second-begin: "))
y2 = float(input("Second-end: "))
int_1 = integral(f1,x1,y1,1e-6)
int_2 = integral(f2,x2,y2,1e-6)
print(int_1)
print(int_2)
It doesn't work correct. Help, please!
You implemented the math wrong. The error is in the lines
for i in range(n):
sum = sum + f(a + i * h)
range(n) always starts at 0, so in your first iteration you just add the f(a) term again.
If you replace it with
for i in range(1, n):
sum = sum + f(a + i * h)
it works.
Also, you have a ton of redundant code; you basically coded the core of the integration algorithm twice. Try to follow the DRY-principle.
The trapezoidal rule of integration simply says that an approximation to the integral $\int_a^b f(x) dx$ is (b-a) (f(a)+f(b))/2. The error is proportional to (b-a)^2, so that it is possible to have a better estimate using the composite rule, i.e., subdividing the initial interval in a number of shorter intervals.
Is it possible to use shorter intervals and still reuse the function values previously computed, so minimizing the total number of function evaluation?
Yes, it is possible if we divide each interval in two equal parts, so that at stage 0 we use 1 intervals, at stage 1 2 equal intervals and in general, at stage n, we use 2n equal intervals.
Let's start with a simple problem and see if it possible to generalize the procedure…
a, b = 0, 32
L = b-a = 32
by the trapezoidal rule the initial approximation say I0, is given by
I0 = L * (f0+f1)/2
= L * S0
with S0 = (f0+f1)/2; a pictorial representation of the real axis, the coordinates of the interval extremes and the evaluated functions follows
x0 x1
01234567890123456789012345679012
f0 f1
Next, we divide the original interval in two,
L = L/2
x0 x2 x1
01234567890123456789012345679012
f0 f2 f1
and the new approximation, stage n=1, is obtained using two times the trapezoidal rule and applying a bit of algebra
I1 = L * (f0+f2)/2 + L * (f2+f1)/2
= L * [(f0+f1)/2 + f2]
= L * [S0 + S1]
with S1 = f2
Another subdivision, stage n=2, L = L/2 and
x0 x3 x2 x4 x1
012345678901234567890123456789012
f0 f3 f2 f4 f1
I2 = L * [(f0+f3) + (f3+f2) + (f2+f4) + (f4+f1)] / 2
= L * [(f0+f1)/2 + f2 + (f3+f4)]
= L * [S0+S1+S2]
with S2 = f3 + f4.
It is not difficult, given this picture,
x0 x5 x3 x6 x2 x7 x4 x8 x1
012345678901234567890123456789012
f0 f5 f3 f6 f2 f7 f4 f8 f1
to understand that our next approximation can be computed as follows
L = L/2
S3 = f5+f6+f7+f8
I3 = L*[S0+S1+S2+S3]
Now, we have to understand how to compute a generalization of Sn,
n = 1, … — for us, the pseudocode is
L_n = (b-a)/2**n
list_x_n = list(a + L_n + 2*Ln*j for j=0, …, 2**n-1)
Sn = Sum(f(xj) for each xj in list_x_n)
For n = 3, L = (b-a)/8 = 4, we have from the formula above list_x_n = [4, 12, 20, 28], please check with the picture...
Now we are ready to code our algorithm in Python
def trapaezia(f, a, b, tol):
"returns integ(f, (a,b)), estimated error and number of evaluations"
from math import fsum # controls accumulation of rounding errors in sums
L = b - a
S = (f(a)+f(b))/2
I = L*S
n = 1
while True:
L = L/2
new_points = (a+L+j*L for j in range(0, n+n, 2))
delta_S = fsum(f(x) for x in new_points)
new_S = S + delta_S
new_I = L*new_S
# error is estimated using Richardson extrapolation (REP)
err = (new_I - I) * 4/3
if abs(err) > tol:
n = n+n
S, I = new_S, new_I
else:
# we return a better estimate using again REP
return (4*new_I-I)/3, err, n+n+1
If you are curious about Richardson extrapolation, I recommend this document that deals exactly with the application of REP to the trapezoidal rule quadrature algorithm.
If you are curious about math.fsum, the docs don't say too much but the link to the original implementation that also includes an extended explanation of all the issues involved.

Quadratic formula find value for x1 and x2 given an equation

Given a nested list l containing coefficient values, I'm trying to calculate the quadratic formula to find zeros of x, denoted as x1,x2. I have a for loop that loops through this list and gives me the value for a,b and c from the nested list:
import math as m
l = [[1,2,1],[9,12,4],[1,-7,0],[1,2,-3]]#nested list
for x in l:
q = x[1]*x[1]-4*x[0]*x[2] #b*b - 4*a*c
q_sr = m.sqrt(q)#root of q
x1 = (-x[1] + q_sr)/(2*x[0])#[1]=b and [0]=a
x2 = (-x[1] - q_sr)/(2*x[0])#[1]=b and [0]=a
eq = x[0]**2 + 2*x[1] + 1*x[2] #equation that im trying to get the x1 and x2
print("a verdier: ", x[0])
print("b verdier: ", x[1])
print("c verdier: ", x[2])
print("x1 verdier: ", x1)
print("x2 verdier: ", x2)
Here, x[0],x[1] and x[2] are the corresponding positions in the list l, e.g., 0 = a, 1=b and 2=c. This all works and i get the right values for x1 and x2.
I'm having trouble calculating the zeroes (x1, x2). How do I calculate these values?
The complex math module is great for things like this.
import cmath
def quadratic(a, b, c):
d = float(b**2 - 4*a*c)
x1 = ((-b)-cmath.sqrt(d))/(2*a)
x2 = ((-b)+cmath.sqrt(d))/(2*a)
return [x.real if (x.imag == 0.0) else x for x in [x1, x2]]
For fun
class Quadratic:
def __init__(self, a, b, c):
self.a, self.b, self.c = a, b, c
self.d = float(self.b ** 2 - 4*self.a*self.c)
self.x1 = ((-b)-cmath.sqrt(self.d))/(2*a)
self.x2 = ((-b)+cmath.sqrt(self.d))/(2*a)
#property
def solution(self):
return [x.real if x.imag == 0.0 else x for x in [self.x1, self.x2]]
def __str__(self):
return "X1 = {}, X2 = {}".format(*self.solution)
myList = [[1, 2, 1], [9, 12, 4], [1, -7, 0], [1, 2, -3]]
for _ in myList:
print Quadratic(*_)
Here is a modified and commented version of you code that should help you understand what you did.
from math import sqrt
coef_list = [[1,2,1],[9,12,4],[1,-7,0],[1,2,-3]]
# This following "for loop" will compute solutions x1 and x2
# for any quadratic equation summarized in your coef_list. In your
# coef_list you have the following equations:
# y(x) = 1*x^2 + 2*x + 1
# y(x) = 9*x^2 + 12*x + 4
# ...
# y(x) = 1*x^2 + 2*x -3
for coef in coef_list:
a, b, c = coef # extract a, b and c from the inner lists
q = b**2 - 4*a*c
# In case q > 0 you have two solutions
if q > 0:
q_sqrt = sqrt(q)
x1 = (-b + q_sqrt)/(2*a)#[1]=b and [0]=a
x2 = (-b - q_sqrt)/(2*a)#[1]=b and [0]=a
# In case q = 0 you have only one solution
elif q == 0:
x1 = -b/(2*a)
x2 = x1
# In case q < 0 you have no real solution
else:
raise ValueError("q is negative.")
# print at all iteration of the loop to have solutions for every
# equation in given in coef_list
print "x1 = ", x1
print "x2 = ", x2
print "a = ", a, ", b = ", b, "and c = ",c
print "-----"
# You don't need the next line since the equation you are trying to solve is
# is defined in coef_list at line 0 (i.e. coef_list[0])
#eq = x[0]**2 + 2*x[1] + 1*x[2] #equation that im trying to get the x1 and x2

Categories