I have to write an script in Fortran that returns the results of the Newton binomial:
for a, b and n given.
The problem is that I cant use functions or subroutines.
Until now I have written the code for the combinations:
if (n==0) then
print*, "Cnk=",Cnk
else if ((n>=0).and.(k==0)) then
print*, "Cnk=",Cnk
else
do i=1,n,1
aux=aux*i
if (k==i) then
factK=aux
end if
if ((n-k)==i) then
factnk=aux
end if
factn=aux
end do
Cnk=factn/(factk*factnk)
print*, "Cnk=",Cnk
end if
In the case of the binomial k is variable from 0 to n.
Probably not the fastest solution, but quite short:
program binom
implicit none
integer,parameter :: N=5
integer,parameter :: a=3
integer,parameter :: b=5
integer :: k, i
integer :: coeff, eval, total
total = 0
do i=0,N
coeff = product((/ (k,k=1,n) /)) / product((/ (k,k=1,i),(k,k=1,n-i) /))
eval = coeff * a**(n-i) * b**i
total = total + eval
write(*,*) 'i=',i,'coeff=',coeff, 'eval=',eval
enddo !i
write(*,*) '(a+b)**n=',(a+b)**N,'Total=',total
end program binom
Output:
i= 0 coeff= 1 eval= 243
i= 1 coeff= 5 eval= 2025
i= 2 coeff= 10 eval= 6750
i= 3 coeff= 10 eval= 11250
i= 4 coeff= 5 eval= 9375
i= 5 coeff= 1 eval= 3125
(a+b)**n= 32768 Total= 32768
Related
First of all, Happy New Year 2022.
I am new in learning OMP Parallel programming in Fortran.
Need HELP with my codes below, which produces a correct result if run in serial,
but produces a random (incorrect) result if run in parallel (multi-threads).
Purpose is to swap the values of some array elements in global shared array A(N:N).
Cannot figure out or identify what is wrong in my code below
(i.e. any race conditions in updating elements of Array A ?)
SUBROUTINE SWAP_COLUMNS (IS)
INTEGER :: IS, L, LH, KK
INTEGER :: I, J
L = (IS - 1) / 2
LH = L + 1
!$OMP PARALLEL DEFAULT(PRIVATE) SHARED(A,IS,L,LH)
!$OMP DO
DO J = 1, L
DO I = 1, IS
KK = I + IS
IF (I == LH) THEN
CALL SWAP_VALUES (I, J+1, KK, J+1)
ELSE
CALL SWAP_VALUES (I, J, KK, J)
END IF
END DO
END DO
!$OMP END DO
!$OMP END PARALEL
SUBROUTINE SWAP_VALUES (I3, K1, I4, K2)
INTEGER :: IT, I3,K1, I4,K2
IT = A(I3, K1)
A(I3, K1) = A(I4,K2)
A(I4, K2) = IT
END SUBROUTINE
Example :
Array A (6,6) :
8 1 6 ….
3 5 7 ….
4 9 2 …
35 28 33 …
30 32 34 …
31 36 29 …
I would expect that the result would always be correct (as run with single thread)
with IS = 3 , the correct expected result :
35 1 6 ….
3 32 7 ….
31 9 2 …..
8 28 33 ….
30 5 34 ….
4 36 29 ...
I tried a manual calculation :
IS = 3
Iteration 1 : J = 1, I = 1, KK = 4
CALL SWAP_VALUES (1, 1, 4, 1) ----> Swap A(1,1) & A(4,1)
Iteration 2 : J = 1, I = 2, KK = 5
CALL SWAP_VALUES (2, 2, 5, 2) -----> Swap A(2,2) & A(5,2)
Last Iteration 3 : J =1, I = 3, KK = 6
CALL SWAP_VALUES( 3,1, 6,1) -----> A(3,1) & A(6,1)
I would expect that even run in parallel (multi threads), as each element of array A
is accessed or updated independently (not dependent on the loop iteration
(before or after), there should be no issue of race condition, and the local variable IT
is a private variable in subroutine SWAP_VALUES.
But instead, I get a random result, sometimes correct and sometimes incorrect,
and sometimes the element value does not swap, or sometimes it is copied but
not swapped so the value becomes duplicated ( e.g. A(1,1) = 35 and A(4,1) = 35
instead of 8)
I was trying to write code with this output:
1
1 2 1
1 2 3 2 1
1 2 3 4 3 2 1
This is what I did :
program pascal
implicit none
integer i,j,k,p,n
write (*, '("input n: ")', advance="no")
read(*,*) n
do i=0,n-1
p=1
do j=1,n-1-i
write(*,'(3X)', advance="no")
enddo
do k = 0,i
write(*,'(I6)', advance="no") p
p = p*(i-k)/(k+1)
enddo
write(*, '(/)')
end do
endprogram
which the output is pascal triangle. How can I fix it?
I'm very new to using Fortran, and I can't seem to figure out why this subroutine is getting stuck in an infinite loop. Here's the code for said DO loop:
SUBROUTINE FILLARRAY(K, N)
REAL X, Y
INTEGER XPOS, YPOS
INTEGER K(N,N)
DO 10 I = 1, 100
15 CALL RANDOM_NUMBER(X)
CALL RANDOM_NUMBER(Y)
XPOS = 20 * X + 1.0
YPOS = 20 * Y + 1.0
PRINT *, XPOS
PRINT *, YPOS
IF(K(XPOS, YPOS).NE.1) THEN
K(XPOS,YPOS) = 1
END IF
IF (K(XPOS, YPOS).EQ.1) THEN
GOTO 15
END IF
10 CONTINUE
RETURN
END
I am basically trying to fill a 20 x 20 array randomly with the value 1.
I was also wondering if there is a way to forego using END IF that anyone knows about! Thank you!
The array will eventually all be set to 1 leading to an infinte loop with GOTO 15.
Try this code instead:
IF(K(XPOS, YPOS).NE.1) THEN
K(XPOS,YPOS) = 1
ELSE
GOTO 15
END IF
This method is horribly inefficient. I'd do it something like the below. Note i've filled with i rather than 1, partially to show the random order of filling, partially to act as a check I haven't screwed up, as each number should appear exactly once.
ian#eris:~/work/stack$ cat random_fill.f90
Program random_fill
Implicit None
Integer, Parameter :: n = 5
Integer, Dimension( 1:n, 1:n ) :: K
Call fillarray( k, n )
Write( *, '( 5( 5( i2, 1x ) / ) )' ) K
Contains
Subroutine fillarray( k, n )
Implicit None
Integer , Intent( In ) :: n
Integer, Dimension( 1:n, 1:n ), Intent( Out ) :: K
Integer, Dimension( : ), Allocatable :: index_list
Real :: rand
Integer :: val, x, y
Integer :: i
index_list = [ ( i, i = 0, n * n - 1 ) ]
Do i = 1, n * n
Call Random_number( rand )
val = 1 + Int( rand * Size( index_list ) )
x = 1 + index_list( val ) / n
y = 1 + Mod( index_list( val ), n )
K( x, y ) = i
index_list = [ index_list( :val - 1 ), index_list( val + 1: ) ]
End Do
End Subroutine fillarray
End Program random_fill
ian#eris:~/work/stack$ gfortran -O -Wall -Wextra -pedantic -fcheck=all -std=f2008 random_fill.f90
ian#eris:~/work/stack$ ./a.out
11 8 14 24 16
19 23 25 15 3
21 20 5 7 18
6 17 22 12 9
2 4 1 10 13
ian#eris:~/work/stack$ ./a.out
24 15 7 22 25
8 17 10 1 14
9 5 4 12 2
11 21 20 3 18
6 19 23 13 16
ian#eris:~/work/stack$ ./a.out
22 11 6 21 24
7 3 8 10 25
17 19 16 2 9
13 4 15 5 23
12 1 14 20 18
You are stuck in an infinite loop because the statement goto 15 is always executed.
If k(xpos, ypos) is 1 then the first if statement is false, but the second is true so the goto 15 is executed.
If instead k(xpos, ypos) is not 1 then the first if statement is true, and so k(xpos, ypos) is set to 1. The second if statement is only evaluated after this, and so is true, and so the goto 15 is executed.
As other answers have mentioned, the method you are using is horribly inefficient. However, if you still want to use it, here is the fixed code, with a number of modernisations:
subroutine fillarray(k, n)
implicit none
integer, intent(in) :: n
integer, intent(inout) :: k(n,n)
real(dp) :: x, y
integer :: xpos, ypos
integer :: i
i=1
do while (i<=100)
call random_number(x)
call random_number(y)
xpos = 20*x + 1.0_dp
ypos = 20*y + 1.0_dp
if (k(xpos, ypos)/=1) then
k(xpos, ypos) = 1
i = i+1
endif
enddo
end subroutine
Note that this assumes that the array k has already been initialised, otherwise checking the contents of the array will lead to undefined behaviour.
As to whether end if is optional or not. No, it is not optional. It is always required. All languages need to know where the end of a loop is. C uses }, Python uses un-indentation, Fortran uses endif.
I have concerns using the Subarray type. I'm trying to transfer a part of global domain (represented by a 2D array) between two procs. I have no problem achieving this without the sub-array structure. The following example illustrate what I want to do. A whole 2D domain is equally divided into two parts for each MPI processus, one containing "zero" (left) and the other containing "one" (right). On each MPI-processus, the half-domain is made of the "real domain" plus a border of guard cells (that's why the array indexing begin at 1-ist, see below). The objective is simple : right domain has to send it's two first columns into the two "guard cells" columns of the left one.
The code that works is the followng :
PROGRAM TEST
USE mpi
IMPLICIT NONE
INTEGER*4, PARAMETER :: ist = 2 ! Guard cells
INTEGER*4, PARAMETER :: nx = 5, ny = 2 ! Domain size
INTEGER*4, DIMENSION (1-ist:nx+ist,1-ist:ny+ist) :: prim ! A vector
INTEGER*4, DIMENSION (1:ist,1-ist:ny+ist) :: prim_S ! Mini vetctor (Send)
INTEGER*4, DIMENSION (1:ist,1-ist:ny+ist) :: prim_R ! Mini vector (Receive)
! MPI stuff
INTEGER*4, PARAMETER :: ndims = 2
INTEGER*4 :: mpicode, nb_procs, rang, comm, etiquette = 100
LOGICAL, DIMENSION (ndims) :: periods
LOGICAL :: reorganisation
INTEGER*4, DIMENSION (ndims) :: dims
INTEGER*4, DIMENSION (2) :: voisinage
INTEGER*4 :: i, j
!--------------------------------------------------------------------
periods = .FALSE.
reorganisation = .FALSE.
dims(1) = 2
dims(2) = 1
! Initialize MPI
CALL MPI_INIT (mpicode)
CALL MPI_COMM_SIZE (MPI_COMM_WORLD, nb_procs, mpicode)
CALL MPI_COMM_RANK (MPI_COMM_WORLD, rang, mpicode)
WRITE (*,*) "PROCESSUS ", rang, " OK"
! Create topology
CALL MPI_CART_CREATE (MPI_COMM_WORLD, ndims, dims, periods,
& reorganisation, comm, mpicode)
CALL MPI_CART_SHIFT (comm, 0, 1, voisinage(1), voisinage(2),
& mpicode)
! Fill each part of the domain
IF (rang .eq. 0) then
prim = 0
ELSE
prim = 1
END IF
! Print the left side BEFORE communication
IF (rang .eq. 0) then
DO j=1-ist, ny+ist
WRITE (*,*) prim(:,j)
END DO
WRITE(*,*) " "
END IF
IF (rang .eq. 1) then
DO i=1, ist
DO j=1-ist, ny+ist
prim_S(i,j) = prim(i,j)
END DO
END DO
END IF
CALL MPI_BARRIER (MPI_COMM_WORLD, mpicode)
! Communication
IF (rang .eq. 0) then
CALL MPI_RECV (prim_R, size(prim_R), MPI_INTEGER
& , voisinage(2),
& etiquette, comm, mpicode)
END IF
IF (rang .eq. 1) then
CALL MPI_SEND (prim_S, size(prim_S), MPI_INTEGER ,
& voisinage(1),
& etiquette,comm, mpicode)
END IF
IF (rang .eq. 0) then
DO i=nx+1, nx+ist
DO j=1-ist, ny+ist
prim(i,j) = prim_R(i-nx,j)
END DO
END DO
END IF
! Print the left domain AFTER the communication
IF (rang .eq. 0) then
DO j=1-ist, ny+ist
WRITE (*,*) prim(:,j)
END DO
END IF
CALL MPI_FINALIZE(mpicode)
END PROGRAM
So it's working, here is the output after the communication :
0 0 0 0 0 0 0 1 1
0 0 0 0 0 0 0 1 1
0 0 0 0 0 0 0 1 1
0 0 0 0 0 0 0 1 1
0 0 0 0 0 0 0 1 1
0 0 0 0 0 0 0 1 1
The fact is that I don't like this method that much, and as the subarray type looks like created for such purposes, I would like to use it. Here is the code, equivalent as previous :
PROGRAM TEST
USE mpi
IMPLICIT NONE
INTEGER*4, PARAMETER :: ist = 2 ! Guard cells
INTEGER*4, PARAMETER :: nx = 5, ny = 2 ! Domain size
INTEGER*4, DIMENSION (1-ist:nx+ist,1-ist:ny+ist) :: prim ! A vector
! MPI stuff
INTEGER*4, PARAMETER :: ndims = 2
INTEGER*4 :: mpicode, nb_procs, rang, comm, etiquette = 100
LOGICAL, DIMENSION (ndims) :: periods
LOGICAL :: reorganisation
INTEGER*4, DIMENSION (ndims) :: dims
INTEGER*4, DIMENSION (6) :: voisinage
INTEGER*4, DIMENSION (2) :: profil_tab, profil_sous_tab
INTEGER*4 :: i, j
INTEGER*4 :: type_envoi_W, type_envoi_E
INTEGER*4 :: type_reception_W, type_reception_E
!--------------------------------------------------------------------
periods = .FALSE.
reorganisation = .FALSE.
dims(1) = 2
dims(2) = 1
CALL MPI_INIT (mpicode)
CALL MPI_COMM_SIZE (MPI_COMM_WORLD, nb_procs, mpicode)
CALL MPI_COMM_RANK (MPI_COMM_WORLD, rang, mpicode)
WRITE (*,*) "PROCESSUS ", rang, " OK"
CALL MPI_CART_CREATE (MPI_COMM_WORLD, ndims, dims, periods,
& reorganisation, comm, mpicode)
CALL MPI_CART_SHIFT (comm, 0, 1, voisinage(1), voisinage(2),
& mpicode)
profil_tab(:) = SHAPE (prim)
profil_sous_tab(:) = (/ist, ny+2*ist/)
! Envoi W
CALL MPI_TYPE_CREATE_SUBARRAY (2, profil_tab, profil_sous_tab,
& (/ist,0/) , MPI_ORDER_FORTRAN, MPI_DOUBLE_PRECISION
& , type_envoi_W, mpicode)
CALL MPI_TYPE_COMMIT (type_envoi_W, mpicode)
! Reception E
CALL MPI_TYPE_CREATE_SUBARRAY (2, profil_tab, profil_sous_tab,
& (/nx+ist,0/) , MPI_ORDER_FORTRAN, MPI_DOUBLE_PRECISION,
& type_reception_E, mpicode)
CALL MPI_TYPE_COMMIT (type_reception_E, mpicode)
IF (rang .eq. 0) then
prim = 0
ELSE
prim = 1
END IF
IF (rang .eq. 0) then
DO j=1-ist, ny+ist
WRITE (*,*) prim(:,j)
END DO
WRITE(*,*) " "
END IF
CALL MPI_BARRIER (MPI_COMM_WORLD, mpicode)
IF (rang .eq. 0) then
CALL MPI_RECV (prim, 1, type_reception_E, voisinage(2),
& etiquette, comm, mpicode)
END IF
IF (rang .eq. 1) then
CALL MPI_SEND (prim, 1, type_envoi_W, voisinage(1),
& etiquette,comm, mpicode)
END IF
IF (rang .eq. 0) then
DO j=1-ist, ny+ist
WRITE (*,*) prim(:,j)
END DO
END IF
CALL MPI_FINALIZE(mpicode)
END PROGRAM
The output is that weird domain, plus a segmentation fault... :
0 0 0 0 0 0 0 0 0
0 0 0 0 0 1 1 1 1
0 0 0 0 0 0 0 0 0
0 0 0 0 0 1 1 1 1
0 0 0 0 0 0 0 0 0
0 0 0 0 0 1 1 1 1
Program received signal SIGSEGV: Segmentation fault - invalid memory reference.
I guess I'm wrong with the beginning coordinates when I'm creating the subarray types but I don't understand why.
I wish you guys can help me with that! Thanks for reading, it's quite a long post but I tried to be clear.
Oak
Your array type should be composed of MPI_INTEGER, not MPI_DOUBLE_PRECISION.
Your MPI_RECV() call in both cases requires a Status argument.
I have a column matrix with 40 values. Say,
1
4
5
2
4
1
9
.
.
.
2
How can I call every four values and average them until it reaches 40th? I managed to do in the following way but is there a better way? Beste!
i = 1, 4
avg1 = avg + avg(i)
i = 5,8
avg2 = avg + avg(i)
i = 9,12
avg3 = avg + avg(i)
.......
i = 37,40
avg10 = avg + avg(i)
It took me a couple of iterations to get the syntax right, but how about this?
integer, parameter, dimension(*) :: a = [ 1, 4, 5, ..., 2 ]
integer :: i
real, dimension(10) :: avg
avg = [ (sum(a(i * 4 + 1 : (i + 1) * 4)) / 4., i = 0, 9) ]
print *, avg
end
How about that?
program testing
implicit none
integer, dimension(40) :: array
real, dimension(10) :: averages
integer :: i, j, k, aux
array(:) = (/(i, i=1,40)/) ! just values 1 to 40
averages(:) = 0.0
k = 1 ! to keep track of where to store the next average
do i=1,40,4 ! iterate over the entire array in steps of 4
aux = 0 ! just a little helper variable, not really required, but neater I think
do j=i,i+3 ! iterating over 4 consecutive values
aux = aux + array(j)
end do
averages(k) = aux / 4.0
k = k + 1
end do
print *, averages
end program testing
This is the output:
2.500000 6.500000 10.50000 14.50000 18.50000
22.50000 26.50000 30.50000 34.50000 38.50000
Is this what you were looking for?