Find roots of a system of equations to an arbitrary decimal precision - python

Given an initial guess for an array of values x, I am trying to find the root of a system that is closest to x. If you are familiar with finding roots of a system, you will understand that finding a root for a system of equations f satisfies:
0 = f_1(x)
0 = f_2(x)
....
0 = f_n(x)
Where f_i is one particular function within f
There is a package within scipy that will do this exactly: scipy.optimize.newton_krylov. For example:
import scipy.optimize as sp
def f(x):
f0 = (x[0]**2) + (3*(x[1]**3)) - 2
f1 = x[0] * (x[1]**2)
return [f0, f1]
# Nearest root is [sqrt(2), 0]
print sp.newton_krylov(f, [2, .01], iter=100, f_tol=Dc('1e-15'))
>>> [ 1.41421356e+00 3.49544535e-10] # Close enough!
However, I am using the decimal package within python because I am doing extremely precise work. decimal offers more than normal decimal precision. scipy.optimize.newton_krylov returns float-precision values. Is there a way to get my answer at an arbitrarily precise decimal precision?

You could try copying the code in and referenced by scipy.optimize.newton_krylov then modifying it to use decimal values rather than floating point values. This may be difficult and time-consuming, of course.
I have done the equivalent for other situations, but never quite this.

I have found the mpmath module, which contains mpmath.findroot. mpmath uses arbitrary decimal-point precision for all of its numbers. mpmath.findroot will find the nearest root within tolerance. Here is an example of using mpmath for the same problem, to a higher precision:
import scipy.optimize as sp
import mpmath
from mpmath import mpf
mpmath.mp.dps = 15
def mp_f(x1, x2):
f1 = (x1**2) + (3*(x2**3)) - 2
f2 = x1 * (x2**2)
return f1, f2
def f(x):
f0 = (x[0]**2) + (3*(x[1]**3)) - 2
f1 = x[0] * (x[1]**2)
return [f0, f1]
tmp_solution = sp.newton_krylov(f, [2, .01], f_tol=Dc('1e-10'))
print tmp_solution
>>> [ 1.41421356e+00 4.87315249e-06]
for _ in range(8):
tmp_solution = mpmath.findroot(mp_f, (tmp_solution[0], tmp_solution[1]))
print tmp_solution
mpmath.mp.dps += 10 # Increase precision
>>> [ 1.4142135623731]
[4.76620313173184e-9]
>>> [ 1.414213562373095048801689]
[4.654573673348783724565804e-12]
>>> [ 1.4142135623730950488016887242096981]
[4.5454827012374811707063801808968925e-15]
>>> [ 1.41421356237309504880168872420969807856967188]
[4.43894795688326535096068850443292395286770757e-18]
>>> [ 1.414213562373095048801688724209698078569671875376948073]
[4.334910114213471839327827177504976152074382061299675453e-21]
>>> [ 1.414213562373095048801688724209698078569671875376948073176679738]
[4.2333106584123451747941381835420647823192649980317402073699554127e-24]
>>> [ 1.41421356237309504880168872420969807856967187537694807317667973799073247846]
[4.1340924398558139440207202654766836515453497962889870471467483995909717197e-27]
>>> [ 1.41421356237309504880168872420969807856967187537694807317667973799073247846210703885]
[4.037199648296693366576484784520203892002447351324378380584214947262318103197216393589e-30]
The precision can be raised arbitrarily.

Related

How will I convert decimals into accurate fractions or accurate decimals?

For some reason it shows an error message: TypeError: argument should be a string or a Rational instance
import cmath
from fractions import Fraction
#Function
# Quadratic equatrion solver
def solver(a_entry, b_entry, c_entry):
a = int(a_entry)
b = int(b_entry)
c = int(c_entry)
d = (b*b) - (4*a*c)
sol1 = (-b-cmath.sqrt(d)/(2*a))
sol2 = (-b+cmath.sqrt(d)/(2*a))
sol3 = Fraction(sol1)
sol4 = Fraction(sol2)
print(f"Value of x1 = {sol3} and value of x2 = {sol4}")
solver(1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 8, in solver
File "/usr/lib/python3.10/fractions.py", line 139, in __new__
raise TypeError("argument should be a string "
TypeError: argument should be a string or a Rational instance
I am a new programmer and I saw that this code generates a weird number (example: 5.42043240824+0j {inaccurate values})
when i give random values. So I want it to give either an accurate decimal values or in fraction. The fraction method dosen't work for some reason. Can someone please help. Alot of thanks.
2 things wrong in your code :
Use math instead of cmath as cmath is used for complexed values (it will always returns a complexe value, even 1+0j) which is not compatible with Fraction.
Be careful you wrote : (-b-cmath.sqrt(d)/(2*a)) but is should be ((-b-cmath.sqrt(d))/(2*a))
Also, the solution might no exist. For example, resolving 1x^2 + 3x + 10 has no answer (your fonction does not cross x axe). It still has complexe answer(s).
To avoid this you can use a try except to catch errors. OR you can validate that d^2 is greater than 4ac because you can't sqrt negative values (except with complexe values ;) ) :
def solver():
a = int(entry.get())
b = int(entry1.get())
c = int(entry2.get())
d = (b*b) - (4*a*c)
if d < 0:
text = "no real answer ! The function doesn't cross X axe !"
label2.configure(text = text)
else:
sol1 = ((-b-math.sqrt(d))/(2*a))
sol2 = ((-b+math.sqrt(d))/(2*a))
sol3 = Fraction(sol1)
sol4 = Fraction(sol2)
label2.configure(text = f"Value of x1 = {sol3} and value of x2 = {sol4}")
Hope it helps
The issue with sqrt
It appears that you do not want to evaluate the square roots to numerical approximations. But that is exactly what cmath.sqrt and math.sqrt do: they calculate numerical approximations of square roots.
For instance:
import math
print( math.sqrt(2) )
# 1.4142135623730951
If you are not interested in numerical approximations, then I suggest using a library for symbolic calculus. The best-known library for symbolic calculus in python is called sympy. This module has a sympy.sqrt function that will simplify a square root as much as it can, but without returning a numerical approximation:
import sympy
print( sympy.sqrt(9) )
# 3
print( sympy.sqrt(2) )
# sqrt(2)
print( sympy.sqrt(18) )
# 3*sqrt(2)
More information about sympy: https://docs.sympy.org/latest/tutorials/intro-tutorial/intro.html
Other advice
When you write a program, it is most usually a good idea to cleanly separate the parts of the code that deal with algorithms, maths, and logic, from the parts of the code that deal with input and output. I suggest writing two functions, one that solves quadratic equations, and one that does input and output:
import sympy
# returns solutions of a x**2 + b x + c == 0
def solver(a, b, c):
Delta = b*b - 4*a*c
sol1 = (-b - sympy.sqrt(Delta)) / (2*a)
sol2 = (-b + sympy.sqrt(Delta)) / (2*a)
return (sol1, sol2)
# ask for user input and solve an equation
def input_equation_output_solution():
a = int(entry.get())
b = int(entry1.get())
c = int(entry2.get())
sol1, sol2 = solver(a, b, c)
label2.configure(text = f"Value of x1 = {sol1} and value of x2 = {sol2}")

evalf and subs in sympy on single variable expression returns expression instead of expected float value

I'm new to sympy and I'm trying to use it to get the values of higher order Greeks of options (basically higher order derivatives). My goal is to do a Taylor series expansion. The function in question is the first derivative.
f(x) = N(d1)
N(d1) is the P(X <= d1) of a standard normal distribution. d1 in turn is another function of x (x in this case is the price of the stock to anybody who's interested).
d1 = (np.log(x/100) + (0.01 + 0.5*0.11**2)*0.5)/(0.11*np.sqrt(0.5))
As you can see, d1 is a function of only x. This is what I have tried so far.
import sympy as sp
from math import pi
from sympy.stats import Normal,P
x = sp.symbols('x')
u = (sp.log(x/100) + (0.01 + 0.5*0.11**2)*0.5)/(0.11*np.sqrt(0.5))
N = Normal('N',0,1)
f = sp.simplify(P(N <= u))
print(f.evalf(subs={x:100})) # This should be 0.5155
f1 = sp.simplify(sp.diff(f,x))
f1.evalf(subs={x:100}) # This should also return a float value
The last line of code however returns an expression, not a float value as I expected like in the case with f. I feel like I'm making a very simple mistake but I can't find out why. I'd appreciate any help.
Thanks.
If you define x with positive=True (which is implied by the log in the definition of u assuming u is real which is implied by the definition of f) it looks like you get almost the expected result (also using f1.subs({x:100}) in the version without the positive x assumption shows the trouble is with unevaluated polar_lift(0) terms):
import sympy as sp
from sympy.stats import Normal, P
x = sp.symbols('x', positive=True)
u = (sp.log(x/100) + (0.01 + 0.5*0.11**2)*0.5)/(0.11*sp.sqrt(0.5)) # changed np to sp
N = Normal('N',0,1)
f = sp.simplify(P(N <= u))
print(f.evalf(subs={x:100})) # 0.541087287864516
f1 = sp.simplify(sp.diff(f,x))
print(f1.evalf(subs={x:100})) # 0.0510177033783834

Sympy parser doesn't pass the right constant

>>>from sympy.parsing.sympy_parser import (parse_expr, ... standard_transformations, function_exponentiation)
>>> transformations = standard_transformations + (function_exponentiation,)
>>>parse= parse_expr('2x', transformations=transformations)
parse = parse_expr("2x", transformations=transformations)
>>> parse.coeff("x",0)
2
>>> parse.coeff("x")
2
>>> parse = parse_expr("2x+5", transformations=transformations)
>>> parse.coeff("x")
2
>>> parse.coeff("x",0)
5
I am quite new to python and sympy.
The problem here is that any time I want to get the constant 0 it returns the coefficient of x. But this doesn't happen when the constant is not zero(shown in the second equation). I am trying to use this to solve linear equations in which I don't know the user input. But it keeps giving me a wrong answer when there is no constant attached after x.
There is some discussion on Github: https://github.com/sympy/sympy/issues/5657
One way to do it is to convert to a polynomial:
>>> (2*x + 3).as_poly()
Poly(2*x + 3, x, domain='ZZ')
>>> (2*x + 3).as_poly().nth(0)
3
>>> (2*x + 3).as_poly().nth(1)
2
>>> (2*x).as_poly().nth(0)
0
>>> (2*x).as_poly().nth(1)
2
Unfortunately converting to a Poly first is slower.

Find roots of a function a x^n + bx - c = 0 where n isn't an integer with Numpy?

I'm writing a program in python and in it I need to find the roots of a function that is:
a*x^n + b*x -c = 0
where a and b are constants that are calculated earlier in the program but there are several thousand of them.
I need to repeat this equation twice for all values of a and b once with n = 77/27 and once with n = 3.
How can i do this in python?
I checked numpy.roots(p) and that would work for when n = 3 I think. But for n = 77/27 how would I be able to do that?
I think your beast choice is scipy.optimize.brentq():
def f(x, n, a, b, c):
return a * x**n + b * x - c
print scipy.optimize.brentq(
f, 0.0, 100.0, args=(77.0/27.0, 1.0, 1.0, 10.0))
prints
2.0672035922580592
Look here and here.
I'm so proud of myself, I still remember the specifics (without reading the link!) :)
If you don't get that, look here.
I would use fsolve from scipy,
from scipy.optimize import fsolve
def func(x,a,b,c,n):
return a*x**n + b*x - c
a,b,c = 11.,23.,31.
n = 77./27.
guess = [4.0,]
print fsolve(func,guess,args=(a,b,c,n)) # 0.94312258329
This of course gives you a root, not necessarily all roots.
Edit: Use brentq, it's much faster
from timeit import timeit
sp = """
from scipy.optimize import fsolve
from scipy.optimize import brentq
from numpy.random import uniform
from numpy import zeros
m = 10**3
z = zeros((m,4))
z[:,:3] = uniform(1,50,size=(m,3))
z[:,3] = uniform(1,10,m)
def func(x,a,b,c,n):
return a*x**n + b*x - c
"""
s = "[fsolve(func,1.0,args=tuple(i)) for i in z]"
t = "[brentq(func,0.,10.,args=tuple(i)) for i in z]"
runs = 10**2
print 'fsolve\t', timeit(s,sp,number=runs)
print 'brentq\t', timeit(t,sp,number=runs)
gives me,
fsolve 15.5562820435
brentq 3.84963393211
You need a root finding algorithm like Newton's method. All root finding algorithms will work with non-integer powers. They need not even be rational numbers.

Cubic root of the negative number on python

Can someone help me to find a solution on how to calculate a cubic root of the negative number using python?
>>> math.pow(-3, float(1)/3)
nan
it does not work. Cubic root of the negative number is negative number. Any solutions?
A simple use of De Moivre's formula, is sufficient to show that the cube root of a value, regardless of sign, is a multi-valued function. That means, for any input value, there will be three solutions. Most of the solutions presented to far only return the principle root. A solution that returns all valid roots, and explicitly tests for non-complex special cases, is shown below.
import numpy
import math
def cuberoot( z ):
z = complex(z)
x = z.real
y = z.imag
mag = abs(z)
arg = math.atan2(y,x)
return [ mag**(1./3) * numpy.exp( 1j*(arg+2*n*math.pi)/3 ) for n in range(1,4) ]
Edit: As requested, in cases where it is inappropriate to have dependency on numpy, the following code does the same thing.
def cuberoot( z ):
z = complex(z)
x = z.real
y = z.imag
mag = abs(z)
arg = math.atan2(y,x)
resMag = mag**(1./3)
resArg = [ (arg+2*math.pi*n)/3. for n in range(1,4) ]
return [ resMag*(math.cos(a) + math.sin(a)*1j) for a in resArg ]
You could use:
-math.pow(3, float(1)/3)
Or more generally:
if x > 0:
return math.pow(x, float(1)/3)
elif x < 0:
return -math.pow(abs(x), float(1)/3)
else:
return 0
math.pow(abs(x),float(1)/3) * (1,-1)[x<0]
You can get the complete (all n roots) and more general (any sign, any power) solution using:
import cmath
x, t = -3., 3 # x**(1/t)
a = cmath.exp((1./t)*cmath.log(x))
p = cmath.exp(1j*2*cmath.pi*(1./t))
r = [a*(p**i) for i in range(t)]
Explanation:
a is using the equation xu = exp(u*log(x)). This solution will then be one of the roots, and to get the others, rotate it in the complex plane by a (full rotation)/t.
Taking the earlier answers and making it into a one-liner:
import math
def cubic_root(x):
return math.copysign(math.pow(abs(x), 1.0/3.0), x)
The cubic root of a negative number is just the negative of the cubic root of the absolute value of that number.
i.e. x^(1/3) for x < 0 is the same as (-1)*(|x|)^(1/3)
Just make your number positive, and then perform cubic root.
You can also wrap the libm library that offers a cbrt (cube root) function:
from ctypes import *
libm = cdll.LoadLibrary('libm.so.6')
libm.cbrt.restype = c_double
libm.cbrt.argtypes = [c_double]
libm.cbrt(-8.0)
gives the expected
-2.0
numpy has an inbuilt cube root function cbrt that handles negative numbers fine:
>>> import numpy as np
>>> np.cbrt(-8)
-2.0
This was added in version 1.10.0 (released 2015-10-06).
Also works for numpy array / list inputs:
>>> np.cbrt([-8, 27])
array([-2., 3.])
You can use cbrt from scipy.special:
>>> from scipy.special import cbrt
>>> cbrt(-3)
-1.4422495703074083
This also works for arrays.
this works with numpy array as well:
cbrt = lambda n: n/abs(n)*abs(n)**(1./3)
Primitive solution:
def cubic_root(nr):
if nr<0:
return -math.pow(-nr, float(1)/3)
else:
return math.pow(nr, float(1)/3)
Probably massively non-pythonic, but it should work.
I just had a very similar problem and found the NumPy solution from this forum post.
In a nushell, we can use of the NumPy sign and absolute methods to help us out. Here is an example that has worked for me:
import numpy as np
x = np.array([-81,25])
print x
#>>> [-81 25]
xRoot5 = np.sign(x) * np.absolute(x)**(1.0/5.0)
print xRoot5
#>>> [-2.40822469 1.90365394]
print xRoot5**5
#>>> [-81. 25.]
So going back to the original cube root problem:
import numpy as np
y = -3.
np.sign(y) * np.absolute(y)**(1./3.)
#>>> -1.4422495703074083
I hope this helps.
For an arithmetic, calculator-like answer in Python 3:
>>> -3.0**(1/3)
-1.4422495703074083
or -3.0**(1./3) in Python 2.
For the algebraic solution of x**3 + (0*x**2 + 0*x) + 3 = 0 use numpy:
>>> p = [1,0,0,3]
>>> numpy.roots(p)
[-3.0+0.j 1.5+2.59807621j 1.5-2.59807621j]
New in Python 3.11
There is now math.cbrt which handles negative roots seamlessly:
>>> import math
>>> math.cbrt(-3)
-1.4422495703074083

Categories

Resources