Newton method for transcendental equation - python

Transcendental equation:
tan(x)/x + b = 0, where b is any real number.
I need to introduce n and give me n solutions of this equation.
My code (Python):
from math import tan, cos, pi, sqrt, sin,exp
import numpy as np
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
def f(x,b):
return tan(x)/x + b
def f1(x,b):
return (x/(cos(x)*cos(x)) - tan(x))/(x**2)
e = 0.00000000001
def newtons_method(x0, f, f1, e):
x0 = float(x0)
while True:
x1 = x0 - (f(x0,b) / f1(x0,b))
if abs(x1 - x0) < e:
return x1
x0 = x1
result = []
n = int(input("Input n: "))
b = float(input("Input b: "))
for i in range(2,4*n,1):
result.append(newtons_method(i, f , f1, e))
lambda_result = sorted(list(set(result)))
print(len(lambda_result))
I change the initial approximation with step 1.The roots are repeated with a period of ~pi, so the second argument 4*n. I exclude repeated solutions through set. If n is 50 then he finds only 18 solution. What needs to be fixed to make it work? Help me, please.

In your cross-post https://math.stackexchange.com/q/2942400/115115 it was strongly recommended by Yves Daoust to base your Newton method on the function
f(x)=sin(x) + b*x*cos(x)
or
f(x)=sin(x)/x + b*cos(x)
as these functions do not have poles or other singularities (if x is not close to 0).
The solutions are, at least for large c, close to the initial values (i+0.5)*pi for i in range(n). The result does then not require sorting or shortening of the result.
One could use that at the roots of the cosine the sine term has alternating sign. This makes a perfect situation to apply a bracketing method like regula falsi (illinois), Dekker's fzeroin, Brent's method,... These methods are almost as fast as the Newton method and are guaranteed to find the root inside the interval.
The only complication is the interval (0,pi/2) as there will be a non-zero root for b<-1. One has to remove the root-finding process from x=0 which is non-trivial for b close to -1.
Newton's method only zero's in to the root reliably when the initial point is close enough to the root. If the point is farther away, close to an extremum of the function, the root of the tangent may be very far away. Thus to successfully apply the Newton method, one needs to find good root approximations from the start. To that end one may use globally convergent fixed-point iterations or structurally simple approximations of the function under consideration.
Using the contracting fixed-point iteration: the solution around k*pi is also a root of the equivalent equation x+arctan(b*x)=k*pi. This gives the approximate solution x=g(k*pi)=k*pi-arctan(b*k*pi). As the arcus tangent is rather flat even for small k, this gives a good approximation.
If b<-1 there is a positive root for k=0, that is in the interval (0,pi/2). The previous method does not work in this case as the arcus tangent has a slope around 1 in this interval. The root is due to the higher order, non-linear terms of the equation, so one needs a non-linear approximation of one of the equivalent forms of the equation. Approximating tan(x)=x/(1-(2*x/pi)^2) gives the same poles at +-pi/2 and is sufficiently close in between. Inserting this approximation into the given equation and solving gives the initial root approximation at x=pi/2*sqrt(1+1/b).
In the implementation one has to shift the root set for b<-1 to include the additional first solution.
from math import tan, cos, pi, sqrt, sin, atan
def f(x,b):
return sin(x)/x+b*cos(x)
def f1(x,b):
return cos(x)/x-(b+x**-2)*sin(x)
e = 1e-12
def newtons_method(x0, f, f1, e):
x0 = float(x0)
while True:
x1 = x0 - (f(x0,b) / f1(x0,b))
if abs(x1 - x0) < e:
return x1
x0 = x1
result = []
n = int(input("Input n: "))
b = float(input("Input b: "))
for i in range(n):
k=i;
if b >= -1: k=k+1
x0 = pi/2*sqrt(1+1/b) if k==0 else k*pi-atan(b*k*pi)
result.append(newtons_method(x0, f , f1, e))
lambda_result = sorted(list(set(result)))
print(len(result), len(lambda_result))

Related

How to solve a system of nonlinear factorial equations in Python

equations
alpha=0.05,beta=0.1,p1=0.015,p2=0.1
Solve for the unknowns n and c!
At a first look, some information is missing regarding how your application defines an acceptable solution, but I am going to make some assumptions and provide a (somewhat naive) approximate answer.
Context first: the problem, posed as is, is a set of 2 algebraic equations in 2 unknowns, to be solved on the 2D grid of non-negative integers. I am assuming you meant the unknowns are non-negative integers, and not, say, real numbers, from the problem formulation, involving the combinatorial terms. Further, note that whenever n is smaller than c, the summation will eventually involve factorial operations on a negative number, formally undefined, hence I will further suggest assuming that we solve over the triangular domain of non-negative integer pairs where n is greater than or equal to c.
As such, to refine the definition of solution, one would have to specify an allowed numeric tolerance (requiring exact equality or allowing, from your application point of view, some error). Unless there is some guarantee, from the problem domain, of the existence of integers solving exactly, I am proceeding to assume that an approximation with known error is meaningful/valuable. Finally: I am going to assume you are looking for A solution (not all solutions) and I am going to limit the search space, as you will see in the simple script below.
With the above said, I don't have any algebraic manipulation to suggest that would lead to a solution, and am not aware of a commercial/open source solver that can solve this non-linear problem over an integer grid. Surprisingly, choosing a reasonable grid and just exhaustively checking some options, yields the following approximate solution:
Opt n: 52, opt c: 2, min_err (residual L2 norm): 0.0074985711238005955
This of course does not claim that there cannot be more accurate approximations for some part of the grid not visited. Such guarantees, if at all, can be achieved with further analysis, such as monotone behavior if the right hand side of the equations w.r.t n and c, etc.
To arrive at the simple approximation suggested above, I treated your right hand sides as functions of n and c, and performed some exhaustive evaluations, keeping track of the error w.r.t to the left hand side required value. The error is then defined by the L2 norm of the error vector - simply the difference between the RHS evaluated at the current location, and the LHS of each of the equations. This is done by the following script, which of course is not scalable for large values of n and c, and the fact that the closest candidate was in the interior of the search space, for all I know, was just chance:
import math
import numpy as np
def eval_eq(n, c, p):
res = 0
for d in range(c + 1):
res += (math.factorial(n) / (math.factorial(d) * (math.factorial(n - d)))) * (p ** d) * ((1. - p) ** (n - d))
return res
def eval_err_norm(n, c):
p1 = 0.015
p2 = 0.1
beta = 0.1
alpha = 0.05
eq1_val = eval_eq(n, c, p1)
eq2_val = eval_eq(n, c, p2)
err1 = eq1_val - (1. - alpha)
err2 = eq2_val - beta
l2_err = np.linalg.norm(np.array([err1, err2]))
return l2_err
if __name__ == '__main__':
err = np.inf
opt_n = None
opt_c = None
for n_ in range(100):
for c_ in range(100):
if n_ < c_:
continue
else:
cur_err = eval_err_norm(n_, c_)
if cur_err < err:
err = cur_err
opt_n = n_
opt_c = c_
print(f'Opt n: {opt_n}, opt c: {opt_c}, min_err (residual L2 norm): {err}')

Derivatives in python

I am trying to find the coefficients of a finite series, $f(x) = \sum_n a_nx^n$. To get the $m$th coefficient, we can take the $m$th derivative evaluated at zero. Therefore, the $m$th coefficient is
$$
a_n = \frac{1}{2\pi i } \oint_C \frac{f(z)}{z^{n+1}} dz
$$
I believe this code takes the derivative of a function using the above contour integral.
import math
import numpy
import matplotlib.pyplot as plt
def F(x):
mean=10
return math.exp(mean*(x.real-1))
def p(n):
mean=10
return (math.pow(mean, n) * math.exp(-mean)) / math.factorial(n)
def integration(func, a, n, r, n_steps):
z = r * numpy.exp(2j * numpy.pi * numpy.arange(0, 1, 1. / n_steps))
return math.factorial(n) * numpy.mean(func(a + z) / z**n)
ns = list(range(20))
f2 = numpy.vectorize(F)
plt.plot(ns,[p(n) for n in ns], label='Actual')
plt.plot(ns,[integration(f2, a=0., n=n, r=1., n_steps=100).real/math.factorial(n) for n in ns], label='Numerical derivative')
plt.legend()
However, it is clear that the numerical derivative is completely off the actual values of the coefficients of the series. What am I doing wrong?
The formulas in the Mathematics Stack Exchange answer that you're using to derive the coefficients of the power series expansion of F are based on complex analysis - coming for example from Cauchy's residue theorem (though other derivations are possible). One of the assumptions necessary to make those formulas work is that you have a holomorphic (i.e., complex differentiable) function.
Your definition of F gives a function that's not holomorphic. (For one thing, it always gives a real result for any complex input, which isn't possible for a non-constant holomorphic function.) But it's easily fixed to be holomorphic, while continuing to return the same result for real inputs.
Here's a fixed version of F, which replaces x.real with x. Since the input to exp is now complex, it's also necessary to use cmath.exp instead of math.exp to avoid a TypeError:
import cmath
def F(x):
mean=10
return cmath.exp(mean*(x-1))
After that fix for F, if I run your code I get rather surprisingly accurate results. Here's the graph that I get. (I had to print out the values to double check that that graph really did show two lines on top of one another.)

Alternatives to bounded root finding functions

My question involves a bounded root finding problem for when the
function behaves strangely when it is near zero.
Setup
I am trying to find the root for the following equation
where
kappa, chi, theta and alpha are all parameters that I set. the
variable k is a 30 by 30 grid.
I am trying to find the root for h evaluating at all k grid points.
The root should return an h that is between the bounds 0 and 1. The
function is increasing, but in areas when h is close to zero (usually
less than 0.1) it will cross the x-axis twice. This occurs for some
grid points that I evaluate at and others it does not happen. In
theory this shouldn't happen. So when I evaluate at grid points using
bisection methods like bisect and brentq at most grid points it works
but for some I will get an error "f(a) and f(b) positive".
So I instead trying to turn the root finding problem in a minimization
problem where I square the function and use brent function. But I am
still running into similar problems like before. However this time it
will stop because of too many iterations and many of the solutions are
not in the specified bounds.
for i in range(nk):
for j in range(nk):
def foch(n):
return (kappa*n**chi - (kgrid[i]**(alpha)*n**(1-alpha) +
(1-delta)*kgrid[i] -
kgrid[j])**(-theta)*(1-alpha)*(kgrid[i])**(alpha)*n**(-alpha))**2
res[i,j] =opt.brent(foch, brack=(10e-3, 1))
Question
How can I find the root to this equation which in theory is unique? Is
there an alternative method that I could use than standard
root-finding or minimazation problems?
Code
import numpy as np
import scipy.optimize as opt
##### parameters #####
beta, alpha, delta, theta, chi = 0.988, 0.321, 0.013, 1, 0.5
#number of grid points.
nk = 30
#setting up the grid
kstar = (1/3)*(alpha/(1/beta - (1-delta)))**(1/(1 - alpha))
kmin=0.25*kstar
kmax=1.75*kstar
kgrid = np.linspace(kmin, kmax, nk)
#set ss labor hours
nstar=.2925
cstar = kstar**alpha*nstar**(1-alpha) - delta*kstar
#endogenously determine kappa
kappa=(1/nstar**chi)*cstar**(-theta)*(1-alpha)*(kstar/nstar)**alpha
#### solve ####
res=np.empty((nk, nk))
for i in range(nk):
for j in range(nk):
def foch(n):
return (kappa*n**chi - (kgrid[i]**(alpha)*n**(1-alpha) +
(1-delta)*kgrid[i] -
kgrid[j])**(-theta)*(1-alpha)*(kgrid[i])**(alpha)*n**(-alpha))**2
res[i,j] =opt.brent(foch, brack=(10e-3, 1))

Solving a non-linear equation in python: the answer is the same as initial guess

So I have this complicated equation which I need to solve. I think that finally x should be of order 1E22. But the problem with this code is that it crashes my entire system. Is there a fix? I tried scipy.optimize.root but it doesn't really solve anything at this order of magnitude (it gives final answer as initial guess without any iteration).
from scipy.optimize import fsolve
import math
import mpmath
import scipy
import sympy
from sympy.solvers import solve
from sympy import Symbol
from sympy import sqrt,exp
x = Symbol('x',positive=True)
cs = 507.643E-12
esi = 1.05E-10
q = 1.6E-19
T = 300
k = 1.381E-23
ni = 1.45E16
print(solve(exp(x/((2*cs/(esi*q))**2)) - ((x/ni)**(esi*k*T)),x))
def func(N):
return (math.exp(N/math.pow(2*cs/(esi*q),2)) - math.pow(N/ni,(esi*k*T)))
n_initial_guess = 1E21
n_solution = fsolve(func, n_initial_guess)
print ("The solution is n = %f" % n_solution)
print ("at which the value of the expression is %f" % func(n_solution))
print(scipy.optimize.root(func, 1E22,tol=1E-10))
Neither of the scipy functions work. The sympy function crashes my laptop. Would Matlab be ideal for this?
Numeric solution with SciPy
The problem that SciPy has with this equation is the loss of significance. You are raising N to the tiny power esi*k*T which makes it very near 1; in floating point arithmetics, it becomes exactly 1. Similarly, the part coming from the exponential becomes 1. Then the two parts are subtracting, leaving 0 - equation appears to be already solved. You could have seen this happening by printing func(1E21) -- it returns 0.
The way to deal with the loss of significance is to rewrite the equation, from the original form
exp(x/((2*cs/(esi*q))**2)) == (x/ni)**(esi*k*T)
by raising both sides to the power 1/(esi*k*T):
exp(x*esi*q**2/(2*cs*k*T)**2)) == x/ni
So func becomes
def func(N):
return np.exp(N*esi*q**2/(k*T*(2*cs)**2)) - (N/ni)
(It's is advisable to use NumPy functions with SciPy solvers.) That said, the solvers, for example root(func, 1E10), will report being unable to converge to a solution.
Symbolic solution with SymPy
SymPy is for solving equations analytically. It does not care for a bunch of floating point numbers. Give it a symbolic equation instead:
x, a, b, c = symbols('x, a, b, c', positive=True)
sol = solve(exp(x/a) - (x/b)**c, x)[0]
The solution is obtained as -c*LambertW(-a/(b*c))/a. Then it can be evaluated:
cs = 507.643E-12
esi = 1.05E-10
q = 1.6E-19
T = 300
k = 1.381E-23
ni = 1.45E16
print(sol.evalf(subs={a: (2*cs/(esi*q))**2, b: ni, c: esi*k*T}))
Which prints -21301663061.0653 - 4649834682.69762*I confirming what one would already expect from the failure of convergence with SciPy: there are no real solutions of the equation.

3d integral, python, integration set constrained

I wanted to compute the volume of the intersect of a sphere and infinite cylinder at some distance b, and i figured i would do it using a quick and dirty python script. My requirements are a <1s computation with >3 significant digits.
My thinking was as such:
We place the sphere, with radius R, such that its center is at the origin, and we place the cylinder, with radius R', such that its axis is spanned in z from (b,0,0). We integrate over the sphere, using a step function that returns 1 if we are inside the cylinder, and 0 if not, thus integrating 1 over the set constrained by being inside both sphere and cylinder, i.e. the intersect.
I tried this using scipy.intigrate.tplquad. It did not work out. I think its because of the discontinuity of the step function as i get warnings such the following. Of course, i might just be doing this wrong. Assuming i have not made some stupid mistake, I could attempt to formulate the ranges of the intersect, thus removing the need for the step function, but i figured i might try and get some feedback first. Can anyone spot any mistake, or point towards some simple solution.
Warning: The maximum number of
subdivisions (50) has been achieved.
If increasing the limit yields no
improvement it is advised to analyze
the integrand in order to determine
the difficulties. If the position of
a local difficulty can be
determined (singularity,
discontinuity) one will probably
gain from splitting up the interval
and calling the integrator on the
subranges. Perhaps a special-purpose
integrator should be used.
Code:
from scipy.integrate import tplquad
from math import sqrt
def integrand(z, y, x):
if Rprim >= (x - b)**2 + y**2:
return 1.
else:
return 0.
def integral():
return tplquad(integrand, -R, R,
lambda x: -sqrt(R**2 - x**2), # lower y
lambda x: sqrt(R**2 - x**2), # upper y
lambda x,y: -sqrt(R**2 - x**2 - y**2), # lower z
lambda x,y: sqrt(R**2 - x**2 - y**2), # upper z
epsabs=1.e-01, epsrel=1.e-01
)
R=1
Rprim=1
b=0.5
print integral()
Assuming you are able to translate and scale your data such a way that the origin of the sphere is in [0, 0, 0] and its radius is 1, then a simple stochastic approximation may give you a reasonable answer fast enough. So, something along the lines could be a good starting point:
import numpy as np
def in_sphere(p, r= 1.):
return np.sqrt((p** 2).sum(0))<= r
def in_cylinder(p, c, r= 1.):
m= np.mean(c, 1)[:, None]
pm= p- m
d= np.diff(c- m)
d= d/ np.sqrt(d** 2).sum()
pp= np.dot(np.dot(d, d.T), pm)
return np.sqrt(((pp- pm)** 2).sum(0))<= r
def in_sac(p, c, r_c):
return np.logical_and(in_sphere(p), in_cylinder(p, c, r_c))
if __name__ == '__main__':
n, c= 1e6, [[0, 1], [0, 1], [0, 1]]
p= 2* np.random.rand(3, n)- 2
print (in_sac(p, c, 1).sum()/ n)* 2** 3
Performing a triple adaptive numerical integrations on a discontinuous function that is constant over two domains is a terribly poor idea, especially if you wish to see either speed or accuracy.
I would suggest a far better idea is to reduce the problem analytically.
Align the cylinder with an axis, by transformation. This translates the sphere to some point that is not at the origin.
Now, find the limits of intersection of the sphere with the cylinder along that axis.
Integrate over that axis variable. The area of intersection at any fixed value along the axis is simply the area of intersection of two circles, which in turn is simply computable using trigonometry and a little effort.
In the end, you will have an exact result, with almost no computation time needed.
I solved it using a simple MC integration, as suggested by eat, but my implementation was to slow. My requirements had increased. I therefore reformulated the problem mathematically, as suggested by woodchips.
Basically i formulated the limits of x as a function of z and y, and y as a function of z. Then i, in essence, integrated f(z,y,z)=1 over the intersection, using the limits. I did this because of the speed increase, allowing me to plot volume vs b, and because it allows me to integrate more complex functions with relative minor modification.
I include my code in case anyone is interested.
from scipy.integrate import quad
from math import sqrt
from math import pi
def x_max(y,r):
return sqrt(r**2-y**2)
def x_min(y,r):
return max(-sqrt(r**2 - y**2), -sqrt(R**2 - y**2) + b)
def y_max(r):
if (R<b and b-R<r) or (R>b and b-R>r):
return sqrt( R**2 - (R**2-r**2+b**2)**2/(4.*b**2) )
elif r+R<b:
return 0.
else: #r+b<R
return r
def z_max():
if R>b:
return R
else:
return sqrt(2.*b*R - b**2)
def delta_x(y, r):
return x_max(y,r) - x_min(y,r)
def int_xy(z):
r = sqrt(R**2 - z**2)
return quad(delta_x, 0., y_max(r), args=(r))
def int_xyz():
return quad(lambda z: int_xy(z)[0], 0., z_max())
R=1.
Rprim=1.
b=0.5
print 4*int_xyz()[0]
First off: You can calculate the volume of the intersection by hand. If you don't want to (or can't) do that, here's an alternative:
I'd generate a tetrahedral mesh for the domain and then add up the cell volumes. An example with pygalmesh and meshplex (both authored by myself):
import pygalmesh
import meshplex
import numpy
ball = pygalmesh.Ball([0, 0, 0], 1.0)
cyl = pygalmesh.Cylinder(-1, 1, 0.7, 0.1)
u = pygalmesh.Intersection([ball, cyl])
mesh = pygalmesh.generate_mesh(u, cell_size=0.05, edge_size=0.1)
points = mesh.points
cells = mesh.cells["tetra"]
# kick out unused vertices
uvertices, uidx = numpy.unique(cells, return_inverse=True)
cells = uidx.reshape(cells.shape)
points = points[uvertices]
mp = meshplex.MeshTetra(points, cells)
print(sum(mp.cell_volumes))
This gives you
and prints 2.6567890958740463 as volume. Decrease cell or edge sizes for higher precision.

Categories