Newton-Raphson Method Using Fortran 90 - fortran

I developed the code below for finding the roots of the given polynomial. It works fine, but I need to adapt it to find all roots and not simply stop when it converges. How do I go about doing this? I thought about creating an outer do loop for values of x, but I'm uncertain whether this is the right approach. Any help is appreciated, thanks in advance.
PROGRAM nr
integer :: i
real :: x, f, df
write(*,*) "x=?"
read (*,*) x
write (*,*) '# Initial value: x=',x
do i=1,100
f= x**4 - 26*(x**3) + 131*(x**2) - 226*x + 120
df = 4*(x**3) - 3.0*26*(x**2) + 2.0*131*x - 226
write (*,*) i,x,f,df
x = x-f/df
end do
write (*,*) '#x = ',x
END PROGRAM

A possible algorithm to find all roots of the polynomial P consists in:
Start from some X0 and find a root R, using Newton's algorithm.
Divide P by (X-R): the division is exact (up to numerical error) since R is a root. (this step is called deflation)
Restart from the beginning if the quotient has degree > 1.
There are some subtleties:
If your polynomial has real coefficients and X0 is real, you will only find a real root, if there is any. To find complex roots, you then have to start from a complex X0, and of course use complex arithmetic.
Not all values of X0 will guarantee convergence, as Newton-Raphson is a local method. See Newton-Kantorovitch theorem and basins of attraction of the Newton method.
If there are multiple roots, convergence there is much slower. There are ways to adapt Newton's method to deal with this.
Numerical errors introduced in the deflation step add up and usually leads to poor accuracy of the subsequent roots. This is especially a problem for polynomials of high degree (how much high really depends). In extreme cases, the computed roots can be quite far from the exact roots. Furthermore, some polynomials are more "difficult" than others: see for instance Wilkinson's polynomial.
There are ways to find information on the roots (bound on the absolute values, circles enclosing the roots...), this can help to pick a good X0 in the algorithm: see Geometrical properties of polynomial roots. If you are only looking for real roots of a real polynomials, you first find bounds, then use Sturm's theorem to find intervals enclosing the roots. Another approach in the real case is to first find the roots of the derivative P' (and to find its roots, first find roots of P'', etc.), as the roots of P' separate the roots of P (except for multiple roots).
Another suggested reading: Roots of polynomials. Note that there are much better algorithms than Newton+deflation. There are also algorithms designed to find all the roots.
However, if you are only interested in this specific polynomial, I suggest to first have a look at the problem, as it's much easier than the general case: WolframAlpha.
Here, starting with integer values of X0 is going to work quite well...

Anyway, I give you below what I think is a better Newton-Raphson code, using Fortran-90 function declaration and a "while" rather than a blind "do". Best, Fabio M. S. Lima
PROGRAM Newton2
! Newton-Raphson method in Fortran-90
implicit none
integer i,imax
real x,xnew,tol
parameter (imax = 30)
parameter (tol = 0.00001)
print*, '*** Initial value: '
read*,x
xnew = 0.5 !Just to begin the loop below
i=0
do while ((abs(xnew-x)>tol) .and. (i<imax))
i=i+1
x = xnew
xnew = x -f(x)/f1(x)
print*, i,xnew,f(xnew)
end do
if (i>imax-2) then
print*,'# Error: Newton-Raphson has not converged after ',i,' iterations!'
else
print*,'* After ',i,' iterations, we found a root at ',xnew
endif
contains
function f(z)
real z,f
f = z**4 -26.0*(z**3) +131.0*(z**2) -226.0*z +120.0
end function f
function f1(z)
real z,f1
f1 = 4.0*(z**3) -3.0*26.0*(z**2) +2.0*131.0*z -226.0
end function f1
END PROGRAM Newton2

Related

How to verify in Fortran whether an iterative formula of a non-linear system will converge

How to verify in Fortran whether an iterative formula of a non-linear system will converge to the root near (x,y)?
It was easy for a programming language which support symbolic computations. But how to do that in Fortran? Like getting partial derivative of the component functions and check whether they bounded near the root. But I couldn't do that in fortran or haven't the idea how to do that. It will be a great help for me if anyone give me some idea for the following non-linear system now or if possible for a general case.
I want to use Fixed point iteration method for this case
Main system:
x^2+y=7
x-y^2=4
Iterative form (given):
X(n+1)=\sqrt(7-Y(n)),
Y(n+1)=-\sqrt(X(n)-4),
(x0,y0)=(4.4,1.0)
Theorem (which I follow)
The issue is, I need to check the boundedness of the partial derivatives of \sqrt(7-Y) and -\sqrt(X-4) on some region around (x0,y0)=(4.4,1.0). I can write the partial derivative function in fortran but how to evaluate so many values and check it is bounded around the (4.4,1.0).
Update
One possibly right solution would be to get arrays of values around (4.4,1.0) like (4.4-h,1.0-h)*(4.4+h,1.0+h) and evaluate the defined partial derivative function and approximate their boundedness. I haven't encounter such problem in Fortran, so any suggestion on that also can help me a lot.
If you just want to check the boundedness of a function on a grid, you can do something like
program verify_convergence
implicit none
integer, parameter :: dp = selected_real_kind(15, 307)
real(dp) :: centre_point(2)
real(dp) :: step_size(2)
integer :: no_steps(2)
real(dp) :: point(2)
real(dp) :: derivative(2)
real(dp) :: threshold
integer :: i,j
real(dp) :: x,y
! Set fixed parameters
centre_point = [4.4_dp, 1.0_dp]
step_size = [0.1_dp, 0.1_dp]
no_steps = [10, 10]
threshold = 0.1_dp
! Loop over a 2-D grid of points around the centre point
do i=-no_steps(1),no_steps(1)
do j=-no_steps(2),no_steps(2)
! Generate the point, calculate the derivative at that point,
! and stop with a message if the derivative is not bounded.
point = centre_point + [i*step_size(1), j*step_size(2)]
derivative = calculate_derivative(point)
if (any(abs(derivative)>threshold)) then
write(*,*) 'Derivative not bounded'
stop
endif
enddo
enddo
write(*,*) 'Derivative bounded'
contains
! Takes a co-ordinate, and returns the derivative.
! Replace this with whatever function you actually need.
function calculate_derivative(point) result(output)
real(dp), intent(in) :: point(2)
real(dp) :: output(2)
output = [sqrt(7-point(2)), -sqrt(point(1)-4)]
end function
end program
I know the function calculate_derivative doesn't do what you want it to, but I'm not sure what function you actually want from your question. Just replace this function as required.
The main question is different: How can you calculate the solution of the mathematical problem without the help of any software? If you know that, we can program it in fortran or any language.
In particular, and assuming that n=0,1,2,3... to solve your problem you need to know X(0) and Y(0). With this you calculate
X(1)=\sqrt(7-Y(0)),
Y(1)=-\sqrt(X(0)-4)
Now you know X(1) and Y(1), then you can calculate
X(2)=\sqrt(7-Y(1)),
Y(2)=-\sqrt(X(1)-4)
etc.
If your system of equations converge to something, until some iterations (for example, n=10, 20, 100) you going the check that. But because the nature of fortran, it will not give you the solution in a symbolic way, it is not its goal.

Time dependent Schrödinger equation queer solution

I am solving the evolution of time dependent schrodinger equation under a harmonic potential and an initial gaussian wavefunction . Treating hcut=1 and 2m=1, and seperating the wavefunction in real and imaginary parts, two coupled equations are obtained in terms of the real and imaginary part, termed as yr and yc respectively.
xrange is [xi,xf]
trange is [0,tf]
method I used is :
first seperating the wavefunction in real and imaginary part, namely yr(x,t) and yc(x,t) .
then treating hcut=2m=1, and writing the wavefunction as yr[x,t]+i*yc[x,t],we get two coupled equations from the TDSE .
1.D[yr[x,t],t]=-D[yc[x,t],x,x]+V[x]*yc[x,t]
2.D[yc[x,t],t]=D[yc[x,t],x,x]-V[x]*yc[x,t]
Then I specified the inital wavefunction as
yr[x,0]=exp[-x^2]
yc[x,0]=0
After that, using finite difference scheme, I tried to find y[x,t] from y[x,0]
i.e,
yr[x,t+d]=yr[x,t]+d*D[yr[x,t],t]
=yr[x,t]+d*(D[yc[x,t],x,x]+V[x]*yc[x,t])
indexed as "a" in code and "b" for the complex part .
The values of y[x[i],t[j]] are stored as y[i,j] as array cannot have real index .
The code I used is given below:
function v(x) result(s)
real::s,x
s=x**2
end function v
real::t(10000),x(10000),yc(10000,10000),yr(10000,10000),tf,xi,xf,d
integer::i,j,k,l,m,n,time
write(*,*) "time of plot divided by step size"
read(*,*) time
tf=50
xi=-10
xf=10
d=0.01
x(1)=xi
t(1)=0
i=1
do while(x(i).lt.xf) !input all values of x in x(i) array
x(i+1)=x(i)+d
i=i+1
end do
n=1
do while(t(n).lt.tf) !input all values of t in t(i) array
t(n+1)=t(n)+d
n=n+1
end do
do j=1,i
yr(j,1)=exp(-(x(j)-5)**2) !input of initial wavefunction
yc(j,1)=0
end do
!calculation of wavefunction at higher time using finite element technique[y[x,t+d]=y[x]+d*D[y[x,t],t] and then replacing the partial derivative with time
!using equation 1 and 2 .
l=1
do while(l.le.i-2)
k=1
do while(t(k).lt.tf)
yr(l,k+1)=yr(l,k)-(yc(l+2,k)-2*yc(l+1,k)+yc(l,k))/d&
+v(x(l))*yc(l,k)*d
yc(l,k+1)=yc(l,k)+(yr(l+2,k)-2*yr(l+1,k)+yr(l,k))/d&
-v(x(l))*yr(l,k)*d
k=k+1
end do
l=l+1
end do
open(1,file="q.dat")
do m=1,i-2
write(1,*) x(m),(yr(m,time))**2+(yc(m,time))**2
end do
close(1)
end
Expected result: y(x,t)^2=yr(x,t)^2+yc(x,t)^2
Error: The wavefunction is not staying regular, after only t=0.05 or 0.06, the wavefunction is turning huge and the maxima is becoming of the order of e30, in spite of that the gaussian shape is remaining almost unchanged, as expected, as only 0.05 seconds has passsed.
This is not really a coding problem but a numerical mathematical one and I suggest you to first study some tutorials about numerical methods for the Schrödinger equation and similar PDEs and then ask for more at sites that are devoted to scientific computation.
First, the method you chose does not conserve the norm of the solution. That can be saved by normalizing by each time step, but it is quite ugly.
Second, the method you chose is explicit (actually, it is probably the forward Euler method that is unconditionally unstable). That means the allovable time step is going to be severly limited by diffusive-like term (even though it is complex here). A simple implicit method that also conserves the norm quite well is the Crank-Nicolson method. It is an implicit method and you will have to solve a system of linear equations at each timestep. It is quite simple in 1D as the system is tridiagonal. There are also some explicit schemes that may work, but they are more involved, not the naive scheme you tried.
Third, you do not have to split the system into the real part and the imaginary part, it can be done using complex numbers.
Fourth, you shoould keep your spatial grid step dx and time-step dt as separate variables with known values. You can compute the final coefficient d as their ratio, but you should at least know your values od dx and dt.
Fifth, you do not have to store all values of the solution in all time-steps. That will quickly become prohibitevely expenses in times of memory required for larger problems. It is enough to store the last time-step and the current time-step. More for multistep methods and some auxiliary steps for Runge-Kutta methods.

Using Function Block in Fortran - Newton Raphson

I am trying apply the function block for Newton Raphson and am a bit lost. My script is suppose to ask the user for an initial X as i've hardcoded the equation. My output file seems to just be stating zero's instead of the proper output listing it's convergence. I'm assuming i'm calling my write statement wrong or too early? Any help is appreciated.
On a side note...is it possible to also ask the user for both the equation and init value as in other languages?
program main
implicit none
real :: x0, xn, err
write(*,*) "Please enter an initial guess X0."
read(*,*) x0
write(*,*) "x = ", xn, " error = ", err
end program main
real function f(x0)
real :: x0, xn,
do
xn = x0 - ( (x0**3 - (x0) - 1) / ( ( 3*(x0**2) )-1) )
err = 100*abs( (xn-x0)/x0 )
x0 = xn
if (err < 0.000001)exit
return
end do
end function
In your code you never call function f, so how do you expect your program to work?
Even if you did call f, the variables xn and err that you declare and set in function f are local to that function, and they will not change the values of x0 and xn in the main program. Use a subroutine to pass multiple values back to the main program.
As #Fortranner stated, you need to call the function. You will find your Fortran programming easier if you place your subroutines and functions in a module and use that module from the calling program. For an example of this organization, see Computing the cross product of two vectors in Fortran 90.
Re "is it possible to also ask the user for both the equation and init value"? That is a very general question. If your equation is always a polynomial, you could easily ask the user for the values of the coefficients. Or you could have a list of equations to select from. But entering a totally arbitrary equation at run-time is something like asking to write a scripting language in Fortran -- definitely non-trivial.
I suggest that while developing a program that you use all available warning and checking options of your compiler. For example, using such options gfortran points out that you don't define the return value of f in function f. Which is a hint that the communication between the main program and function has problem(s).

Two point boundary with odeint

I am trying to solve two point boundary problem with odeint. My equation has the form of
y'' + a*y' + b*y + c = 0
It is pretty trivial when I have boundary conditions of y(x_1) = y_1 , y'(x_2) = y_2, but when boundary conditions are y(x_1) = y_1 , y(x_2) = y_2 I am lost. Does anybody know the way to deal with problems like this with odeint or other scientific library?
In this case you need a shooting method. odeint does not have such a method, it solved the initial value problem (IVP) which is your first case. I think in the Numerical Recipies this method is explained and you can use Boost.Odeint to do the time stepping.
An alternative and more efficient method to solve this type of problem is finite differences or finite elements method. For finite differences you can check Numerical Recipes. For finite elements I recommend dealii library.
Another approach is to use b-splines: Assuming you do know the initial x0 and final xfinal points of integration, then you can expand the solution y(x) in a b-spline basis, defined over (x0,xfinal), i.e.
y(x)= \sum_{i=1}^n A_i*B_i(x),
where A_i are constant coefficients to be determined, and B_i(x) are b-spline basis (well defined polynomial functions, that can be differentiated numerically). For scientific applications you can find an implementation of b-splines in GSL.
With this substitution the boundary value problem is reduced to a linear problem, since (am using Einstein summation for repeated indices):
A_i*[ B_i''(x) + a*B_i'(x) + b*B_i(x)] + c =0
You can choose a set of points x and create a linear system from the above equation. You can find information for this type of method in the following review paper "Applications of B-splines in Atomic and Molecular Physics" - H Bachau, E Cormier, P Decleva, J E Hansen and F Martín
http://iopscience.iop.org/0034-4885/64/12/205/
I do not know of any library solving directly this problem, but there are several libraries for B-splines (I recommend GSL for your needs), that will allow you to form the linear system. See this stackoverflow question:
Spline, B-Spline and NURBS C++ library

Efficient convergence check

I have a grid with thousands of double precision reals.
It's iterating through, and I need it to stop when it's reached convergence to 3 decimal places.
The target is to have it run as fast as possible, but needs to give the same result every (to 3 dp) every time.
At the minute I'm doing something like this
REAL(KIND=DP) :: TOL = 0.001_DP
DO WHILE(.NOT. CONVERGED)
CONVERGED = .TRUE.
DO I = 1, NUM_POINTS
NEW POTENTIAL = !blah blah blah
IF (CONVERGED) THEN
IF (NEW_POTENTIAL < OLD_POTENTIAL - TOL .OR. NEW_POTENTIAL > OLD_POTENTIAL + TOL) THEN
CONVERGED = .FALSE.
END IF
END IF
OLD_POTENTIAL = NEW POTENTIAL
END DO
END DO
I'm thinking that many IF statements can't be too great for performance. I thought about checking for convergence at the end; finding the average value (summing the whole grid, divide by num_points), and checking if that has converged in the same way as above, but I'm not convinced this will always be accurate.
What is the best way of doing this?
If I understand correctly you've got some kind of time-stepping going on, where you create the values in new_potential by calculations on old_potential. Then make old equal to new and carry on.
You could replace your existing convergence tests with the single statement
converged = all(abs(new_potential - old_potential)<tol)
which might be faster. If the speed of the test is a major concern you could test only every other (or every third or fourth ...) iteration
A few comments:
1) If you used a potential array with 2 planes, instead of an old_ and new_potential, you could transfer new_ into old_ by swapping indices at the end of each iteration. As your code stands there's a lot of data movement going on.
2) While semantically you are right to have a while loop, I'd always use a do loop with a maximum number of iterations, just in case the convergence criterion is never met.
3) In your declaration REAL(KIND=DP) :: TOL = 0.001_DP the specification of DP on the numerical value of TOL is redundant, REAL(KIND=DP) :: TOL = 0.001 is adequate. I'd also make this a parameter, the compiler may be able to optimise its use if it knows that it is immutable.
4) You don't really need to execute CONVERGED = .TRUE. inside the outermost loop, set it before the first iteration -- this will save you a nanosecond or two.
Finally, if your convergence criterion is that every element in the potential array has converged to 3dp then that is what you should test for. It would be relatively easy to construct counterexamples for your suggested averages. However, my concern would be that your system will never converge on every element and that you should be using some matrix norm computation to determine convergence. SO is not the place for a lesson in that topic.
What are the calculations for the convergence criteria? Unless they are worse then the calculations to advance the potential it is probably better to have the IF statement to terminate the loop as soon as possible rather than guess a very large number of iterations to be sure to obtain a good solution.
Re High Performance Mark's suggestion #1, if the copying operation is a significant portion of the run time, you could also use pointers.
The only way to be sure about this stuff is to measure the run time ... Fortran provides intrinsic functions to measure both CPU and clock time. Otherwise you may modify your some portion of you code to make it faster, perhaps making it less easier to understand and possibly introducing a bug, possibly without much improvement in runtime ... if that portion was taking a small amount of the total runtime, no amount of cleverness will can make much difference.
As High Performance Mark says, though the current semantics are elegant, you probably want to guard against an infinite loop. One approach:
PotentialLoop: do i=1, MaxIter
blah
Converged = test...
if (Converged) exit PotentialLoop
blah
end do PotentialLoop
if (.NOT. Converged) write (*, *) "error, did not converge"
I = 1
DO
NEWPOT = !bla bla bla
IF (ABS(NEWPOT-OLDPOT).LT.TOL) EXIT
OLDPOT = NEWPOT
I = MOD(I,NUMPOINTS) + 1
END DO
Maybe better
I = 1
DO
NEWPOT = !bla bla bla
IF (ABS(NEWPOT-OLDPOT).LT.TOL) EXIT
OLDPOT = NEWPOT
IF (I.EQ.NUMPOINTS) THEN
I = 1
ELSE
I = I + 1
END IF
END DO