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

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]

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)

How can I print the solutions as a fraction, not like that?

**I am trying to make a function return the results of a Quadratic equation, But I can't figure out how to print the solutions as fraction. Please help me! **
def cube_root(x):
return x**(1/3)
def Quadratic(a, b, c):
delta = (b**2)-4*a*c
if delta == 0:
x = (-b)/2*a
return f"This Quadratic equation has 1 solution: {x}"
else:
if delta < 0 :
return "This Quadratic equation has no solutions: "
else:
x1 = ((-b)-cube_root(delta))/2*a
x2 = ((-b)+cube_root(delta))/2*a
return f"This Quadratic equation has 2 solutions: {x1} & {x2}"
print(Quadratic(12, 0, -1))
You can use simplify from the sympy package (not in the standard library - you'll have to install it):
from sympy import simplify, sqrt
def quadratic(a, b, c):
a = simplify(a) # convert inputs into objects used by simplify
b = simplify(b)
c = simplify(c)
delta = (b**2)-4*a*c
if delta == 0:
x = (-b)/2*a
return f"This Quadratic equation has 1 solution: {x}"
elif delta < 0 :
return "This Quadratic equation has no real solutions: "
else:
x1 = ((-b)-sqrt(delta))/2*a # using sqrt from sympy
x2 = ((-b)+sqrt(delta))/2*a
return f"This Quadratic equation has 2 solutions: {x1} & {x2}"
print(quadratic(12, 0, -1))
This gives:
This Quadratic equation has 2 solutions: -24*sqrt(3) & 24*sqrt(3)
Different example:
print(quadratic(12, 2, -1))
gives:
This Quadratic equation has 2 solutions: -12*sqrt(13) - 12 & -12 + 12*sqrt(13)
Actually sympy can also handle complex numbers for you, so you can get rid of your test for no real solutions (i.e. remove the elif, so that delta < 0 is handled by the else: block).
If you do this and then give it the example:
print(quadratic(12, 2, 1))
you get:
This Quadratic equation has 2 solutions: -12 - 12*sqrt(11)*I & -12 + 12*sqrt(11)*I
If you don't want extra packages, perhaps the following might help:
from fractions import Fraction
def is_square(x):
if x < 0: return False
s = int(x**0.5)
return s*s == x
def sqrt_frac_str(frac):
if frac < 0:
return f'i {sqrt_frac_str(-frac)}'
num_isq = is_square(frac.numerator)
den_isq = is_square(frac.denominator)
if num_isq and den_isq:
return f'{int(frac.numerator**0.5)}/{int(frac.denominator**0.5)}'
elif num_isq:
return f'{int(frac.numerator**0.5)}/sqrt({frac.denominator})'
elif den_isq:
return f'sqrt({frac.numerator})/{int(frac.denominator**0.5)}'
else:
return f'sqrt({frac})'
def quadratic_frac(a, b, c):
delta = Fraction(b**2 - 4 * a * c)
rootcenter = Fraction(-b, 2 * a)
rootdeltasq = delta / Fraction(2 * a)**2
return rootcenter, rootdeltasq
def quadsol_str(rootcenter, rootdeltasq):
return f'{rootcenter} +/- {sqrt_frac_str(rootdeltasq)}'
Tests:
rc, rd = quadratic_frac(2, 1, -1)
rc, rd
# (Fraction(-1, 4), Fraction(9, 16))
quadsol_str(*quadratic_frac(2, 1, -1))
# '-1/4 +/- 3/4'
quadsol_str(*quadratic_frac(2, 0, -1))
# '0 +/- 1/sqrt(2)'
quadsol_str(*quadratic_frac(2, 0, 1))
# '0 +/- i 1/sqrt(2)'
quadsol_str(*quadratic_frac(3, 2, -1))
# '-1/3 +/- 2/3'
quadsol_str(*quadratic_frac(5, 3, -7))
# '-3/10 +/- sqrt(149)/10'

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

Python: Finding multiple roots of nonlinear equation

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)).

Categories