In the following code of array summation give me correct answer, when i use max_rows=10,100,1000,10000 , but when i use max_rows=100000 or more, i am getting abnormal answer and even i am getting negative partial sum from one of the proces.
program sum_vector
use mpi
implicit none
integer,parameter::max_rows=100000
integer::myrank,master=0,ierr,status(mpi_status_size),num_procs
integer::i,rank,avg_rows_per_procs,sender
integer::num_rows_to_send,num_rows_to_receive,start_row,end_row,partial_sum,total_sum,st1,st2
integer,allocatable::vector(:),vector2(:)
allocate(vector(max_rows),stat=st1)
allocate(vector2(max_rows),stat=st2)
if(st1/=0 .or. st2/=0)then
print*,'Cannot allocate'
stop
end if
call mpi_init(ierr)
call mpi_comm_rank(mpi_comm_world,myrank,ierr)
call mpi_comm_size(mpi_comm_world,num_procs,ierr)
if (myrank==0)then
do i=1,max_rows
vector(i)=i
end do
avg_rows_per_procs=max_rows/num_procs
do rank=1,num_procs-1
start_row=rank*avg_rows_per_procs+1
end_row=start_row+avg_rows_per_procs-1
if (rank==num_procs-1)end_row=max_rows
num_rows_to_send=end_row-start_row+1
call mpi_send(num_rows_to_send,1,mpi_int,rank,101,mpi_comm_world,ierr)
call mpi_send(vector(start_row),num_rows_to_send,mpi_int,rank,102,mpi_comm_world,ierr)
end do
total_sum=0
do i=1,avg_rows_per_procs
total_sum=total_sum+vector(i)
end do
print*,'Partial sum=',total_sum,'from root process'
do rank=1,num_procs-1
call mpi_recv(partial_sum,1,mpi_int,mpi_any_source,103,mpi_comm_world,status,ierr)
sender=status(mpi_source)
print*,'Partial sum=',partial_sum,'from rank',sender
total_sum=total_sum+partial_sum
end do
print*,'Total sum=',total_sum
else
call mpi_recv(num_rows_to_receive,1,mpi_int,master,mpi_any_tag,mpi_comm_world,status,ierr)
call mpi_recv(vector2,num_rows_to_receive,mpi_int,master,mpi_any_tag,mpi_comm_world,status,ierr)
partial_sum=0
do i=1,num_rows_to_receive
partial_sum=partial_sum+vector2(i)
end do
call mpi_send(partial_sum,1,mpi_int,master,103,mpi_comm_world,ierr)
end if
call mpi_finalize(ierr)
stop
end program sum_vector
It seems that integer overflow occurs for total_sum and partial_sum for large max_rows because the former become as large as ~ max_rows**2. Changing the declaration to
use iso_fortran_env, only: int64
integer(int64) :: total_sum, partial_sum
and the MPI calls for sending/receiving partial_sum as
call mpi_recv(partial_sum,1,mpi_long_long_int,mpi_any_source,103,mpi_comm_world,status,ierr)
and
call mpi_send(partial_sum,1,mpi_long_long_int,master,103,mpi_comm_world,ierr)
probably gives the expected result. For example, the result obtained with max_rows = 100000 and 4 processes (using gfortran 4.7 and openmpi 1.6.5) is
Partial sum= 312512500 from root process
Partial sum= 937512500 from rank 1
Partial sum= 1562512500 from rank 2
Partial sum= 2187512500 from rank 3
Total sum= 5000050000
and the result with max_rows = 100000000 is
Partial sum= 312500012500000 from root process
Partial sum= 937500012500000 from rank 1
Partial sum= 1562500012500000 from rank 2
Partial sum= 2187500012500000 from rank 3
Total sum= 5000000050000000
This code works as long as max_rows is less than ~ 2*10^9.
Additional notes:
The exact answer is Total sum = max_rows * (max_rows + 1) / 2 (simply a sum from 1 to max_rows).
The maximum number of integer is often approximately 2*10^9 (please see integer), so if max_rows is greater than 10^5, (10^5)^2 / 2 becomes greater than 2*10^9, which may exceed the limit of integer.
Edit: I have changed integer(8) to integer(int64) so that it will be portable (please see #casey's comment).
Related
The following Fortran function takes forever to print the Hello World 2 after printing Hello World 1.
program Test_Long_Run
implicit none
! Variables
integer,allocatable,dimension(:) :: test
integer :: i, j, k, l, m, int
allocate(test(1000*100)); test = 0
! Body of Test_Long_Run
print *, 'Hello World 1'
do k = 1,100
do j = 1,100
do i = 1,100
do m = 1,100
do l = 1,1000
test(l*m) = 2
int = 2
enddo
enddo
enddo
enddo
enddo
print *, 'Hello World 2'
end program Test_Long_Run
It runs very fast if I comment out test(l*m) = 2
The instructions in the inner body perform 100 billion multiplication and assignments to a table, plus an assignment to a variable. The array you write to contains 100 thousand integers, so does not fit into any cache. The elements you write to are not contiguous (stride: 1000*4 bytes), so each write must bypass/invalidate the cache and involve RAM. This is slow. Even hundreds of processor cycles per each assignment. If your processor runs at 2GHz, or at 2 billion instructions per second, you will make at most 10 million assignments per second. Now divide 100 billion by 10 million. 10 thousand seconds?
What happens if you remove the assignment to the array? The program will be left with an assignment to a single variable. Assignment of a constant value. A decent compiler will notice that all these loops can be thrown away and only one instruction will be left: int = 2.
Remember that a compiler has a right to modify your source code if it decides that it will not change the program's behavior.
I am having problems with a do while implementation for a sine taylor series. Editing the do loop to do bb = 1, 10 , 2 gives an expected result well within the margin of error, however when running the desired implementation of the do loop (do while(abs(sineseries) - accuracy > 0), will always give an answer equal to 1. So I have narrowed the possibilities down to the do while loop implementation being faulty.
program taylor
implicit none
real :: x
real :: sineseries, nfactsine
real, parameter :: accuracy = 1.e-10
integer :: signum, bb
nfactsine = 1
signum = 1
write(*,*) "Write your input value"
read(*,*) x
sineseries = 0
do while(abs(sineseries) - accuracy > 0)
sineseries = sineseries + (signum*x**bb)/nfactsine
nfactsine = nfactsine*(bb+1)*(bb+2)
signum = -signum
end do
write(*,*) sineseries, sin(x)
end program taylor
The two types of loops are not doing the same thing.
In the loop
do bb=1, 10, 2
...
end do
you have loop control with variable bb. This variable takes the values 1, 3, ..., 9 at iterations as the loop proceeds.
The do while does not have this control: you must replicate the increment of bb manually:
bb=1
do while (...)
...
bb=bb+2
end do
As Pierre de Buyl commented, you also have an error in the termination condition for the indefinite iteration count. The condition initially evaluates as false, so the loop body isn't executed even once.
So I am using the taylor series to calculate sin(0.75) in fortran 90 up until a certain point, so I need to run it in a do while loop (until my condition is met). This means I will need to use a factorial, here's my code:
program taylor
implicit none
real :: x = 0.75
real :: y
integer :: i = 3
do while (abs(y - sin(0.75)) > 10.00**(-7))
i = i + 2
y = x - ((x**i)/fact(i))
print *, y
end do
end program taylor
Where i've written fact(i) is where i'll need the factorial. Unfortunately, Fortran doesn't have an intrinsic ! function. How would I implement the function in this program?
Thanks.
The following simple function answers your question. Note how it returns a real, not an integer. If performance is not an issue, then this is fine for the Taylor series.
real function fact(n)
integer, intent(in) :: n
integer :: i
if (n < 0) error stop 'factorial is singular for negative integers'
fact = 1.0
do i = 2, n
fact = fact * i
enddo
end function fact
But the real answer is that Fortran 2008 does have an intrinsic function for the factorial: the Gamma function. For a positive integer n, it is defined such that Gamma(n+1) == fact(n).
(I can imagine the Gamma function is unfamiliar. It's a generalization of the factorial function: Gamma(x) is defined for all complex x, except non-positive integers. The offset in the definition is for historical reasons and unnecessarily confusing it you ask me.)
In some cases you may want to convert the output of the Gamma function to an integer. If so, make sure you use "long integers" via INT(Gamma(n+1), kind=INT64) with the USE, INTRINSIC :: ISO_Fortran_env declaration. This is a precaution against factorials becoming quite large. And, as always, watch out for mixed-mode arithmetic!
Here's another method to compute n! in one line using only inline functions:
product((/(i,i=1,n)/))
Of course i must be declared as an integer beforehand. It creates an array that goes from 1 to n and takes the product of all components. Bonus: It even works gives the correct thing for n = 0.
You do NOT want to use a factorial function for your Taylor series. That would meant computing the same terms over and over. You should just multiply the factorial variable in each loop iteration. Don't forget to use real because the integer will overflow quickly.
See the answer under the question of your schoolmate Program For Calculating Sin Using Taylor Expansion Not Working?
Can you write the equation which gives factorial?
It may look something like this
PURE FUNCTION Bang(N)
IMPLICIT NONE
INTEGER, INTENT(IN) :: N
INTEGER :: I
INTEGER :: Bang
Bang = N
IF(N == 2) THEN
Bang = 2
ELSEIF(N == 1) THEN
Bang = 1
ELSEIF(N < 1) THEN
WRITE(*,*)'Error in Bang function N=',N
STOP
ELSE
DO I = (N-1), 2, -1
Bang = Bang * I
ENDDO
ENDIF
RETURN
END FUNCTION Bang
I wrote a simple FORTRAN code in order to do the following: assume we have to integer numbers n1 and n2 which have common divisors. For example 3 and 6 both are divided by 3. Here is the code
PROGRAM test
INTEGER i,n1,n2
WRITE(*,*)' Please enter two numbers: '
READ(*,*)n1,n2
DO i=2,10,1
IF(MOD(n1,i).EQ.0.AND.MOD(n2,i).EQ.0)THEN
n1=n1/i
n2=n2/i
ENDIF
n1=n1
n2=n2
ENDDO
WRITE(*,*)n1,n2
PAUSE
END
This works fine for the example (3,6). However, there are cases like (4,8) in which the numbers have more than one common divisor, in this case 2 and 4. Another example (16,24). I want to compute the maximum common divisor of the two numbers and then reduce them (i.e. 3,6 to 1 and 2), but the code returns the first one (4,8 returns to 2, 4 instead of 1,2). How should it be modified in order to calculate the maximum divisor?
Many thanks in advance!
You could stay with an i, till your if-statement is false.
In other words:
If a number can be divided by i, then don't immediately go to i+1, but try to divide by i again.
EDIT: I think the easiest way is to use a DO WHILE-loop. To calculate the divisor, you have to multiply all your i.
gcd = 1
DO i=2,10,1
DO WHILE (MOD(n1,i).EQ.0.AND.MOD(n2,i).EQ.0)
n1=n1/i
n2=n2/i
gcd = gcd * i
ENDDO
ENDDO
WRITE(*,*) gcd
What you are looking for is the greatest common divisor. You may do this:
function gcd(a, b)
implicit none
integer a, b, aa, bb, cc, gcd
aa = abs(a)
bb = abs(b)
do while (bb .ne. 0)
cc = mod(aa, bb)
aa = bb
bb = cc
end do
gcd = aa
end
Note: it is written in Fortran 77 + MIL-STD-1753 (for the DO WHILE construct and IMPLICIT NONE).
I'm trying to write a program to find the mean, median, mode of an integer array but am having some complications in finding the mode. The following is the code that I've written so far.
First, the program will prompt user to enter a value for the number of integers that will be entered followed by request to enter that number of integers. The integers are then sorted in ascending order and the mean and median are found.
The problem I am having is when I try to get the mode. I am able to count the number of occurrence of a repetitive value. By finding the value with highest occurrence, we'll be able to find Mode. But I am unsure how to do this. Is there any intrinsic function in Fortran to calculate number of occurrence of input values and the value with highest occurrence?
PROGRAM STATISTICS
!Created by : Rethnaraj Rambabu
IMPLICIT NONE
REAL, DIMENSION(:), ALLOCATABLE:: VAL
REAL TEMP, MEDIAN
REAL EVEN, MEAN, SUM, FMODE
INTEGER N, I,J
WRITE(*,*)' WHAT IS THE VALUE FOR N? '
READ(*,*) N
ALLOCATE(VAL(N))
WRITE(*,*) 'ENTER THE NUMBERS'
OPEN(1,FILE='FILE.TXT')
READ(1,*)(VAL(I),I=1,N)
CLOSE(1)
WRITE(*,*) VAL
!/---FOR SORTING----/!
DO I=1,N-1
DO J=1,N-1
IF(VAL(J) > VAL(J+1)) THEN
TEMP=VAL(J)
VAL(J)=VAL(J+1)
VAL(J+1)=TEMP
END IF
END DO
END DO
WRITE(*,*) VAL
!/-----MEDIAN----/!
IF ((N/2*2) /= N) THEN
MEDIAN=VAL((N+1)/2)
ELSE IF ((N/2*2) == N) THEN
EVEN= (VAL(N/2)+VAL((N+2)/2))
MEDIAN=EVEN/2
END IF
WRITE(*,*)'MEDIAN=', MEDIAN
!/----MEAN----/
SUM=0
DO I=1,N
SUM=SUM+VAL(I)
END DO
MEAN=SUM/N
WRITE(*,*)'MEAN=', MEAN
!/------MODE----/
FMODE=1
DO I=1,N-1
IF (VAL(I) == VAL(I+1)) THEN
FMODE=FMODE+1
END IF
END DO
WRITE(*,*)FMODE
END PROGRAM
The FILE.TXT contains
10 8 1 9 8 9 9 7 5 9 3 5 6
But, how to do that? Or is there any intrinsic function in Fortran to calculate number of occurrence of input values and the value with highest occurrence.
No, there is not. You'll have to calculate the mode by hand.
The following code should work (on a sorted array):
FMODE = VAL(1)
COUNT = 1
CURRENTCOUNT = 1
DO I = 2, N
! We are going through the loop looking for values == VAL(I-1)...
IF (VAL(I) == VAL(I-1)) THEN
! We spotted another VAL(I-1), so increment the count.
CURRENTCOUNT = CURRENTCOUNT + 1
ELSE
! There are no more VAL(I-1)
IF (CURRENTCOUNT > COUNT) THEN
! There were more elements of value VAL(I-1) than of value FMODE
COUNT = CURRENTCOUNT
FMODE = VAL(I-1)
END IF
! Next we are looking for values == VAL(I), so far we have spotted one...
CURRENTCOUNT = 1
END
END DO
IF (CURRENTCOUNT > COUNT) THEN
! This means there are more elements of value VAL(N) than of value FMODE.
FMODE = VAL(N)
END IF
Explanation:
We keep the best-so-far mode in the FMODE variable, and the count of the FMODE in the COUNT variable. As we step through the array we count the number of hits that are equal to what we are looking at now, in the CURRENTCOUNT variable.
If the next item we look at is equal to the previous, we simply increment the CURRENTCOUNT. If it's different, then we need to reset the CURRENTCOUNT, because we will now count the number of duplications of the next element.
Before we reset the CURRENTCOUNT we check if it's bigger than the previous best result, and if it is, we overwrite the previous best result (the FMODE and COUNT variables) with the new best results (whatever is at VAL(I) and CURRENTCOUNT), before we continue.
This reset doesn't happen at the end of the loop, so I inserted another check at the end in case the most frequent element happens to be the final element of the loop. In that case we overwrite FMODE, like we would have done in the loop.
It is a bit lengthy, you could probably get rid of the optional argument, but there is an example provided here. They use the quick sort algorithm as implemented here.
Alternatively, you could use
integer function mode(arr) result(m)
implicit none
integer, dimension(:), intent(in) :: arr
! Local variables
integer, dimension(:), allocatable :: counts
integer :: i, astat
character(len=128) :: error_str
! Initialise array to count occurrences of each value.
allocate(counts(minval(arr):maxval(arr)), stat=astat, errmsg=error_str)
if (astat/=0) then
print'("Allocation of counts array failed.")'
print*, error_str
end if
counts = 0
! Loop over inputted array, counting occurrence of each value.
do i=1,size(arr)
counts(arr(i)) = counts(arr(i)) + 1
end do
! Finally, find the mode
m = minloc(abs(counts - maxval(counts)),1)
end function mode
This doesn't require any sorting.