Dimension check of automatic objects in explicit interfaces - fortran

I always assumed that giving values in automatic sized dummy arguments within subroutine definitions has some benefit in error detection, when the routine is defined inside a module that gives an explicit interface. But following example shows that there is not really a checking of array bounds:
module test_mod
Implicit none
contains
!> Testing routine that has an automatic array arr of length n. The integer n is also given.
!> It is included in a module and has an explicit interface.
subroutine print_array(n, arr)
Integer, intent(in) :: n !< Size of arr
Integer, Dimension(n), intent(in) :: arr !< A test array that should be of size n
Integer :: i
do i = 1, n
print *, 'number:', i, arr(i)
enddo
End Subroutine print_array
end module test_mod
program playground
use test_mod, only : print_array
! Call print_array with an array smaller than 3 elements
Call print_array(3, [8, 9])
end program playground
arr is created with the dimension of n in subroutine print_array, but it is not checked if the dimension of the given array is correct. In my example the output is:
number: 1 8
number: 2 9
number: 3 -858993460
So the 3rd value is taken outside of arr bounds and in my case -858993460 is printed. I understand that such behaviour can not generally be catched at compile time. But even using ifort with /check:bounds option doesn't give an error message at run time.
Therefore, I would like to ask if it is good practice to specify arr with Dimension(n) or is it even better to define it with Dimension(:). What is the advantage of giving the dimension in such way?

Related

Should the use of the INTENT keyword speed the code up?

This question is based on an answer to the post Fortran intent(inout) versus omitting intent, namely the one by user Vladimyr, #Vladimyr.
He says that "<...> Fortran copies that data into a contiguous section of memory, and passes the new address to the routine. Upon returning, the data is copied back into its original location. By specifying INTENT, the compiler can know to skip one of the copying operations."
I did not know this at all, I thought Fortran passes by reference exactly as C.
The first question is, why would Fortran do so, what is the rationale behind this choice?
As a second point, I put this behaviour to the test. If I understood correctly, use of INTENT(IN) would save the time spent in copying back the data to th original location, as the compiler is sure the data has not been changed.
I tried this little piece of code
function funco(inp) result(j)
!! integer, dimension(:), intent (in) :: inp
integer, dimension(:):: inp
integer, dimension(SIZE(inp)) :: j ! output
j = 0.0 !! clear whole vector
N = size(inp)
DO i = 1, N
j(i) = inp(i)
END DO
end function
program main
implicit none
interface
function funco(inp) result(j)
!! integer, dimension(:), intent (in) :: inp
integer, dimension(:) :: inp
integer, dimension(SIZE(inp)) :: j ! output
end function
end interface
integer, dimension(3000) :: inp , j
!! integer, dimension(3000) :: funco
integer :: cr, cm , c1, c2, m
real :: rate, t1, t2
! Initialize the system_clock
CALL system_clock(count_rate=cr)
CALL system_clock(count_max=cm)
CALL CPU_TIME(t1)
rate = REAL(cr)
WRITE(*,*) "system_clock rate ",rate
inp = 2
DO m = 1,1000000
j = funco(inp) + 1
END DO
CALL SYSTEM_CLOCK(c2)
CALL CPU_TIME(t2)
WRITE(*,*) "system_clock : ",(c2 - c1)/rate
WRITE(*,*) "cpu_time : ",(t2-t1)
end program
The function copies an array, and in the main body this is repeated many times.
According to the claim above, the time spent in copying back the array should somehow show up.
system_clock rate 1000.00000
system_clock : 2068.07910
cpu_time : 9.70935345
but the results are pretty much the same independently from whether INTENT is use or not.
Could anybody share some light on these two points, why does Fortran performs an additional copy (which seems ineffective at first, efficiency-wise) instead of passing by reference, and does really INTENT save the time of a copying operation?
The answer you are referring to speaks about passing some specific type of subsection, not of the whole array. In that case a temporary copy might be necessary, depending on the function. Your function uses and assumed shape array and a temporary array will not be necessary even if you try quite hard.
An example of what the author (it wasn't me) might have had in mind is
module functions
implicit none
contains
function fun(a, n) result(res)
real :: res
! note the explicit shape !!!
integer, intent(in) :: n
real, intent(in) :: a(n, n)
integer :: i, j
do j = 1, n
do i = 1, n
res = res + a(i,j) *i + j
end do
end do
end function
end module
program main
use functions
implicit none
real, allocatable :: array(:,:)
real :: x, t1, t2
integer :: fulln
fulln = 400
allocate(array(1:fulln,1:fulln))
call random_number(array)
call cpu_time(t1)
x = fun(array(::2,::2),(fulln/2))
call cpu_time(t2)
print *,x
print *, t2-t1
end program
This program is somewhat faster with intent(in) when compared to intent(inout) in Gfortran (not so much in Intel). However, it is even much faster with an assumed shape array a(:,:). Then no copy is performed.
I am also getting some strange uninitialized accesses in gfortran when running without runtime checks. I do not understand why.
Of course this is a contrived example and there are real cases in production programs where array copies are made and then intent(in) can make a difference.

Question regarding the syntax of SGESV in Fortran

I am a bit confused on this subroutine. I have read the documentation but I am a bit confused what exactly the IPIV vector does and how exactly I set my leading dimension. I read that the leading dimension helps to find the starting point for the matrix elements in each successive column of the array. For example lets say we want to solve
Ax = B
where
integer, parameter :: sp = selected_real_kind(6,37)
real(kind=sp),dimension(:,:),intent(inout) :: A
real(kind=sp),dimension(:),intent(inout) :: B
integer, dimension(10) :: IPIV
where sp is for single precision which I have set in my main program
and the dimensions are
A(10,10)
B(10)
which are set in my main program and passed to this subroutine
Should I set my subroutine as
integer :: n,INFO
n = size(A,1)
IPIV = 0
call SGESV(n,n,A,2*n,IPIV,B,2*n,INFO)
or
call SGESV(n,n,A,n,IPIV,B,n,INFO)
and for IPIV I should just create a vector of size 10 and initialize it with zeros?
edit : I have used
call sgesv(n, n, A, n, ipiv, B, n, INFO)
as proposed as well but I get a segmentation error Program received signal SIGSEGV: Segmentation fault - invalid memory reference.
I have printed the matrix sizes and they are correct which are the size of the matrix A is 100 and the size of the vector is 10
Edit2 : So in my main I have a loop which inside my loop it calculates a matrix of A (10,10) and a vector B(10) at each iteration. Then I call my subroutine to solve the system
call solver(A,B)
However I get the segmentation error which I do not understand since the dimensions are correct. (To check it I printed the size of the matrix and the vector and commented out the call to my subroutine and they are 100 and 10)
Perhaps I should make my matrices allocatable? But I do not see a problem with that since at each iteration I calculate the matrix and the vector though a series of calculations and overwrite them.
Basically I declare the matrix and the vector as follows
real(sp) , dimension (10) :: B
real(sp) , dimension (10,10) :: A
then inside my loop a series of calculations are performed to fill them with values
and then I call my subroutine
and then repeat with new values
You are using an old interface to lapack. Note my lower answer for the modern/generic routine.
Old interface
You would call it like
call sgesv(n, n, A, n, ipiv, B, n, info)
Reasoning:
leading dimensions are n and not 2n
ipiv is an output variable s.t. you dont need to initialize it with 0
Modern interface: LAPACK95
It is alot easier to just use the modern interfaces which provide generic calls as such
call gesv(A, B, ipiv=ipiv, info=info)
You dont need to specify the data types (e.g. no more sgesv) nor matrix dimensions.
Make sure that you need to use the appropriate module
use lapack95
Below is an example of calling gesv the generic Lapack95 equivalent (and much simpler) of sgesv and dgesv.
subroutine test_lapack95(n)
use BLAS95
use LAPACK95
use f95_precision
implicit none
integer, intent(in) :: n
real(float), allocatable :: A(:,:), LU(:,:)
real(float), allocatable :: b(:), x(:)
integer, allocatable :: ipiv(:)
allocate(A(n,n))
allocate(b(n))
allocate(ipiv(n))
! Fill values in A and b
call prepare_values(n, A, b)
LU = A
x = b
call gesv(LU,x,ipiv)
! Solve to A*x=b, for x
end subroutine
don't worry about the helper function prepare_values, it just fill in A and b.

Fortran, passing array using starting index only

In the program below, there are two methods presented for passing an array:
program main
integer, dimension(4) :: x = [9, 8, 7, 6]
call print_x(x(2:3)) ! Method 1
call print_x(x(2)) ! Method 2
end program
subroutine print_x(x)
integer, dimension(2), intent(in) :: x
print *, x
end subroutine
Both methods produce the same result: the numbers 8 and 7 are printed. Personally, I would never code this using Method 2 because it looks like a single value is being passed rather than an array.
Can you give an example of when Method 2 MUST be used instead of Method 1?
Consider the program
implicit none
integer :: x(2,2)=0
call set(x(2,1))
print*, x
contains
subroutine set(y)
integer y(2)
y = [1,2]
end subroutine set
end program
The dummy argument y in this subroutine call is argument associated with the elements x(2,1) and x(1,2). There is no array section of x which consists of exactly these two elements.

How to use dimension and declare multiple arrays with different size in same line using Fortran?

I am given this code:
...
IMPLICIT REAL*8(a-h,o-z)
DIMENSION L1(L), L2(M), L3(N)
...
I want to use IMPLICIT NONE but I don't know how to declare variable type using DIMENSION and maintain one line declaration of L1 to L3.
Something like:
INTEGER, DIMENSION :: L1(L), L2(M), L3(N) !(this doesn't work)
The syntax for the dimension statement differs from that of specifying the dimension attribute in a declaration statement.
So, whereas
dimension i(4) ! Implicitly typed
gives i array nature of size 4,
integer, dimension(4) :: i
is the way to go.
Now to come to your question about declaring multiple arrays in one line:
integer i(4), j(5), k(6)
Finally, one can still use
integer, dimension(4) :: i, j(5), k(6), l
making i and l arrays of size 4 and j and k arrays of size 5 and 6.

changing array dimensions in fortran

There are basically two ways to pass arrays to a subroutine in Fortran 90/95:
PROGRAM ARRAY
INTEGER, ALLOCATABLE :: A(:,:)
INTEGER :: N
ALLOCATE(A(N,N))
CALL ARRAY_EXPLICIT(A,N)
! or
CALL ARRAY_ASSUMED(A)
END PROGRAM ARRAY
SUBROUTINE ARRAY_EXPLICIT(A,N)
INTEGER :: N
INTEGER :: A(N,N)
! bla bla
END SUBROUTINE ARRAY_EXPLICIT
SUBROUTINE ARRAY_ASSUMED(A)
INTEGER, ALLOCATABLE :: A(:,:)
N=SIZE(A,1)
! bla bla
END SUBROUTINE ARRAY_ASSUMED
where you need an explicit interface for the second, usually through the use of a module.
From FORTRAN77, I'm used to the first alternative, and I read this is also the most efficient if you pass the whole array.
The nice thing with the explicit shape is that I can also call a subroutine and treat the array as a vector instead of a matrix:
SUBROUTINE ARRAY_EXPLICIT(A,N)
INTEGER :: N
INTEGER :: A(N**2)
! bla bla
END SUBROUTINE ARRAY_EXPLICIT
I wondered if there is a nice way to do that kind of thing using the second, assumed shape interface, without copying it.
See the RESHAPE intrinsic, e.g.
http://gcc.gnu.org/onlinedocs/gfortran/RESHAPE.html
Alternatively, if you want to avoid the copy (in some cases an optimizing compiler might be able to do a reshape without copying, e.g. if the RHS array is not used afterwards, but I wouldn't count on it), as of Fortran 2003 you can assign pointers to targets of different rank, using bounds remapping. E.g. something like
program ptrtest
real, pointer :: a(:)
real, pointer :: b(:,:)
integer :: n = 10
allocate(a(n**2))
a = 42
b (1:n, 1:n) => a
end program ptrtest
I was looking to do the same thing and came across this discussion. None of the solutions suited my purposes, but I found that there is a way to reshape an array without copying the data using iso_c_binding if you are using the fortran 2003 standard which current fortran 90/95 compilers tend to support. I know the discussion is old, but I figured I would add what I came up with for the benefit of others with this question.
The key is to use the function C_LOC to convert an array to an array pointer, and then use C_F_POINTER to convert this back into a fortran array pointer with the desired shape. One challenge with using C_LOC is that C_LOC only works for array that have a directly specified shape. This is because arrays in fortran with an incomplete size specification (i.e., that use a : for some dimension) include an array descriptor along with the array data. C_LOC does not give you the memory location of the array data, but the location of the descriptor. So an allocatable array or a pointer array don't work with C_LOC (unless you want the location of the compiler specific array descriptor data structure). The solution is to create a subroutine or function that receives the array as an array of fixed size (the size really doesn't matter). This causes the array variable in the function (or subroutine) to point to the location of the array data rather than the location of the array descriptor. You then use C_LOC to get a pointer to the array data location and C_F_POINTER to convert this pointer back into an array with the desired shape. The desired shape must be passed into this function to be used with C_F_POINTER. Below is an example:
program arrayresize
implicit none
integer, allocatable :: array1(:)
integer, pointer :: array2(:,:)
! allocate and initialize array1
allocate(array1(6))
array1 = (/1,2,3,4,5,6/)
! This starts out initialized to 2
print *, 'array1(2) = ', array1(2)
! Point array2 to same data as array1. The shape of array2
! is passed in as an array of intergers because C_F_POINTER
! uses and array of intergers as a SIZE parameter.
array2 => getArray(array1, (/2,3/))
! Change the value at array2(2,1) (same as array1(2))
array2(2,1) = 5
! Show that data in array1(2) was modified by changing
! array2(2,1)
print *, 'array(2,1) = array1(2) = ', array1(2)
contains
function getArray(array, shape_) result(aptr)
use iso_c_binding, only: C_LOC, C_F_POINTER
! Pass in the array as an array of fixed size so that there
! is no array descriptor associated with it. This means we
! can get a pointer to the location of the data using C_LOC
integer, target :: array(1)
integer :: shape_(:)
integer, pointer :: aptr(:,:)
! Use C_LOC to get the start location of the array data, and
! use C_F_POINTER to turn this into a fortran pointer (aptr).
! Note that we need to specify the shape of the pointer using an
! integer array.
call C_F_POINTER(C_LOC(array), aptr, shape_)
end function
end program
#janneb has already answered re RESHAPE. RESHAPE is a function -- usually used in an assignment statement so there will be a copy operation. Perhaps it can be done without copying using pointers. Unless the array is huge, it is probably better to use RESHAPE.
I'm skeptical that the explicit shape array is more efficient than the assumed shape, in terms of runtime. My inclination is to use the features of the Fortran >=90 language and use assumed shape declarations ... that way you don't have to bother passing the dimensions.
EDIT:
I tested the sample program of #janneb with ifort 11, gfortran 4.5 and gfortran 4.6. Of these three, it only works in gfortran 4.6. Interestingly, to go the other direction and connect a 1-D array to an existing 2-D array requires another new feature of Fortran 2008, the "contiguous" attribute -- at least according to gfortran 4.6.0 20110318. Without this attribute in the declaration, there is a compile time error.
program test_ptrs
implicit none
integer :: i, j
real, dimension (:,:), pointer, contiguous :: array_twod
real, dimension (:), pointer :: array_oned
allocate ( array_twod (2,2) )
do i=1,2
do j=1,2
array_twod (i,j) = i*j
end do
end do
array_oned (1:4) => array_twod
write (*, *) array_oned
stop
end program test_ptrs
You can use assumed-size arrays, but it can mean multiple layers of wrapper
routines:
program test
implicit none
integer :: test_array(10,2)
test_array(:,1) = (/1, 2, 3, 4, 5, 6, 7, 8, 9, 10/)
test_array(:,2) = (/11, 12, 13, 14, 15, 16, 17, 18, 19, 20/)
write(*,*) "Original array:"
call print_a(test_array)
write(*,*) "Reshaped array:"
call print_reshaped(test_array, size(test_array))
contains
subroutine print_reshaped(a, n)
integer, intent(in) :: a(*)
integer, intent(in) :: n
call print_two_dim(a, 2, n/2)
end subroutine
subroutine print_two_dim(a, n1, n2)
integer, intent(in) :: a(1:n1,1:*)
integer, intent(in) :: n1, n2
call print_a(a(1:n1,1:n2))
end subroutine
subroutine print_a(a)
integer, intent(in) :: a(:,:)
integer :: i
write(*,*) "shape:", shape(a)
do i = 1, size(a(1,:))
write(*,*) a(:,i)
end do
end subroutine
end program test
I am using ifort 14.0.3 and 2D to 1D conversion, I could use an allocatable array for 2D array and a pointer array for 1D:
integer,allocatable,target :: A(:,:)
integer,pointer :: AP(:)
allocate(A(3,N))
AP(1:3*N) => A
As #M.S.B mentioned, in case both A and AP have the pointer attribute, I had to use contiguous attribute for A to guarantee the consistency of the conversion.
Gfortran is a bit paranoid with interfaces. It not only wants to know the type, kind, rank and number of arguments, but also the shape, the target attribute and the intent (although I agree with the intent part). I encountered a similar problem.
With gfortran, there are three different dimension definition:
1. Fixed
2. Variable
3. Assumed-size
With ifort, categories 1 and 2 are considered the same, so you can do just define any dimension size as 0 in the interface and it works.
program test
implicit none
integer, dimension(:), allocatable :: ownlist
interface
subroutine blueprint(sz,arr)
integer, intent(in) :: sz
integer, dimension(0), intent(in) :: arr
! This zero means that the size does not matter,
! as long as it is a one-dimensional integer array.
end subroutine blueprint
end interface
procedure(blueprint), pointer :: ptr
allocate(ownlist(3))
ownlist = (/3,4,5/)
ptr => rout1
call ptr(3,ownlist)
deallocate(ownlist)
allocate(ownlist(0:10))
ownlist = (/3,4,5,6,7,8,9,0,1,2,3/)
ptr => rout2
call ptr(3,ownlist)
deallocate(ownlist)
contains
! This one has a dimension size as input.
subroutine rout1(sz,arr)
implicit none
integer, intent(in) :: sz
integer, dimension(sz), intent(in) :: arr
write(*,*) arr
write(*,*) arr(1)
end subroutine rout1
! This one has a fixed dimension size.
subroutine rout2(sz,arr)
implicit none
integer, intent(in) :: sz
integer, dimension(0:10), intent(in) :: arr
write(*,*) "Ignored integer: ",sz
write(*,*) arr
write(*,*) arr(1)
end subroutine rout2
end program test
Gfortran complains about the interface. Changing the 0 into 'sz' solves the problem four 'rout1', but not for 'rout2'.
However, you can fool gfortran around and say dimension(0:10+0*sz) instead of dimension(0:10) and gfortran compiles and gives the same
result as ifort.
This is a stupid trick and it relies on the existence of the integer 'sz' that may not be there. Another program:
program difficult_test
implicit none
integer, dimension(:), allocatable :: ownlist
interface
subroutine blueprint(arr)
integer, dimension(0), intent(in) :: arr
end subroutine blueprint
end interface
procedure(blueprint), pointer :: ptr
allocate(ownlist(3))
ownlist = (/3,4,5/)
ptr => rout1
call ptr(ownlist)
deallocate(ownlist)
allocate(ownlist(0:10))
ownlist = (/3,4,5,6,7,8,9,0,1,2,3/)
ptr => rout2
call ptr(ownlist)
deallocate(ownlist)
contains
subroutine rout1(arr)
implicit none
integer, dimension(3), intent(in) :: arr
write(*,*) arr
write(*,*) arr(1)
end subroutine rout1
subroutine rout2(arr)
implicit none
integer, dimension(0:10), intent(in) :: arr
write(*,*) arr
write(*,*) arr(1)
end subroutine rout2
end program difficult_test
This works under ifort for the same reasons as the previous example, but gfortran complains about the interface. I do not know how I can fix it.
The only thing I want to tell gfortran is 'I do not know the dimension size yet, but we will fix it.'. But this needs a spare integer arguemnt (or something else that we can turn into an integer) to fool gfortran around.