Fortran DO loop over generic set of indices? - fortran

I have an N-dimensional dataset (say real numbers) which is stored as a 1D array with an additional dimension array which specifies the original dimensions.
In addition, the functions to deduce the 1-D index from an N-D index and vice-versa are given.
I'm trying to figure out how do I make a do-loop (or equivalent) for the generic N-dimensional index (which will be transformed to 1D index of course) from some set of limiting lower indices to a set of upper indices.
So I need an "N-dimensional" loop which does not go over all of the values - only a portion of the array, therefore doing the linear index of an equivalent 1D array is not relevant (at least without modifications).
This is a schematic of my problem:
subroutine Test(Array,Dims,MinIndex,MaxIndex)
implicit none
real , dimension(1:), intent(inout) :: Array
integer, dimension(1:), intent(in) :: Dims,MinIndex,MaxIndex
integer, dimension(size(Dims)) :: CurrInd
integer :: 1Dindex
! size(Dims) can be 1, 2, 3 ,..., N
! size(MinIndex)==size(MaxIndex)==size(Dims)
! size(Array)==Product(Dims)
! 1Dindex=Get1dInd(NDindex,Dims)
! NDindex=GetNdInd(1Dindex,Dims)
! How do I actually preform this?
do CurrInd=MinIndex,MaxIndex
1Dindex=Get1dInd(CurrInd,Dims)
<Some operation>
enddo
end subroutine
I figured it is possible to loop over the Dims array and use an inner loop but I don't manage to write the procedure down properly.
Another option that didn't work for me (maybe because I use it incorrectly?) is FORALL, as that requires each index to be specified separately.

If you knew the dimensions of your arrays at compilation time you could do a series of nested DO loops, each of them running between pairs of components of MinIndex and MaxIndex. Since you don't know the dimensions, that's not possible.
The easiest strategy I can think of is to loop with a single DO loop over all the 1D indices. For each of them, compute the N-dimensional index, and check if it is within the bounds provided by MinIndex and MaxIndex: if it is, continue doing what you need; if it is not, discard that 1D index and go to the next one. If the indices are sequential you may be able to do something smarter that skips blocks of indices that you know you will not be interested in.
do OneDindex = 1, size(Array)
CurrInd = GetNDInd(OneDindex, Dims)
if ((any(CurrInd<MinIndex)) .or. (any(CurrInd>MaxIndex))) cycle
! <Some operation>
end do
Note that, as far as the index manipulation is concerned, this strategy is compatible with parallelising the loop.
Also note that Fortran variables must start with a letter: 1Dindex is not a valid Fortran variable name.

So this is the actual procedure I ended up with.
I have verified it and hope it will be useful to you as it was for me.
(I know this is not well commented, but the question has all the details and my time is extremely short at the moment)
Also, thanks ripero for helping out!
I decided not to use the CYCLE approach as I assume the code will work more efficiently when only the actual amount of loops are performed.
!-----------------------------------------------------------
subroutine Test(Array,Rank,Dims,InitInd,FinInd)
implicit none
real, dimension(1:), intent(inout) :: Array
integer, intent(in) :: Rank
integer, dimension(1:), intent(in) :: Dims
integer, dimension(1:), intent(in) :: InitInd,FinInd
!-----------------------------------------------------------
integer :: nOuter
integer :: i,j, OneDInd
integer, dimension(Rank) :: Curr
!-----------------------------------------------------------
! Check how many repetition for the outer-loop
Curr=FinInd-InitInd
nOuter=1
do i=2,Rank
nOuter=nOuter*(Curr(i)+1)
enddo
!-----------------------------------------------------------
! Actual looping:
Curr=InitInd
do j=1,nOuter
! Update minor indices (>1):
do i=1,Rank
if (Curr(i).GT.FinInd(i)) then
! Update next index:
Curr(i)=InitInd(i)
Curr(i+1)=Curr(i+1)+1
endif
enddo
! Loop over major index:
do i=InitInd(1),FinInd(1)
!OneDInd=Get1dInd(Curr,Dims)
!<operation>
! Advance major index:
Curr(1)=Curr(1)+1
enddo
enddo
end subroutine Test

Related

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 - lbound throws error 6366 "The shapes of the array expressions do not conform"

So I've been confounded again by Fortran. Go figure. Anyway, I'm trying to write a pretty simple routine the strips values off the end of an array. Everything complicated works fine, except I want to write the subroutine such that I don't have to pass the lower bound of the input array to it. Here is the subroutine:
subroutine Strip(list,lstart, index)
implicit none
integer :: i, index, isize, tsize, lstart, istart
real, dimension(:), allocatable, intent(inout) :: list
real, dimension(:), allocatable :: tlist
isize = size(list)
tsize = index-1
print *, 'index', index
print *, 'isize', isize
print*, 'lbound', INT(lbound(list))
print*, 'tsize', tsize
istart = lbound(list) !<---- This lines throws the error
!These are commented out because everything below here works
!allocate(tlist(lstart:tsize))
!tlist = list(lstart:index-1)
!deallocate(list)
!call move_alloc(tlist,list)
end subroutine Strip
Right now I'm passing the lower bound of the input list into the subroutine (lstart), but I'd like not to do that. Anyway, this code doesn't compile, the compiler throws the error 6366: The shapes of the array expressions do not conform [ISTART]
I don't know how to fix this. Any suggestions?
Lbound() returns an array! Read The Fortran Manual (RTFM) at https://gcc.gnu.org/onlinedocs/gfortran/LBOUND.html
It returns an array with as many elements as is the rank ("dimension" 1D,2D,...) of the array.
To get a single number, for a specific dimension, use the optional argument DIM.
istart = lbound(list, 1)

Index multiple non-adjacent elements of a Fortran array

Is there a way in Fortran to access many elements of an array without using a loop?
For example given array of 100 elements
real(100) :: a
can I do something like this to access elements 1,4,7,54,81 that do not follow a regular step?
a(1,4,7,54,81)= 3.21423
you could use a vector subscript: a( (/1,4,7,54,81/) )= 3.21423
As noted before, an array may be used as the indexes of an array. This is a so-called vector subscript.
A([1,4,7,54,81]) = 3.21423
sets the elements given to that value. (This is the same as the earlier answer but using the Fortran 2003+/modern array constructor notation.)
The array can be any rank-1 array, such as a variable or expression:
integer :: idx(5)=[1,4,7,54,81]
A(idx) = 3.21423
A(idx-1+1) = 3.21423
Of course, vector subscripts are of use in other settings, such as referencing:
print *, A(idx)
call sub(A(idx))
A(idx) = A(idx+1) + 5
However, array sections with vector subscripts are subject to various restrictions, such as:
not always may they be arguments to a procedure;
a pointer may not point to them;
not all such sections may be assigned to.
In the third case, if the same index appears more than once in the subscript we can't define it. So
print *, A([1,5,1])
is allowed, but
A([1,5,1]) = 3.
is not.
RESHAPE and WHERE are worth looking at.
If you are determining which elements to 'pull out' then maybe ALLOCATE a new variable and stuff the elements of A into B.
Maybe something like this:
REAL, DIMENSION(100) :: A
LOGICAL, DIMENSION(100) :: A_Mask
INTEGER :: SizeB
REAL, DIMENSION(:), ALLOCATABLE :: B
!...
A_Mask = .FALSE.
WHERE(A > 1.0)
A_Mask = .TRUE.
ENDWHERE
SizeB = SUM(A_Mask)
!... then allocate B and fill it.

Defining a real scalar equal to an 1x1 matrix

Let A and B be matrices of size 1 times n and n times 1, respectively.
Then the multiplication of A with B is a 1 times 1 matrix.
Which is the better way to assign the value of MATMUL(A,B) to a real number x?
I would like to write:
x=MATMUL(A,B) ! <<--- but this is wrong.
The above expression is wrong because I'm trying to assign a 1 times 1 matrix to a real number.
My solution is to define a 1 times 1 matrix C and with this:
C=MATMUL(A,B)
x=C(1,1) ! <--- this solution is ok, but is too long
But, there exists a better way to assign MATMUL(A,B) to the real number x?
The entire code with my question is as follow:
PROGRAM testing
!
IMPLICIT NONE
REAL :: A(1,2),B(2,1),C(1,1),x
!
A(1,1)=1.0; A(1,2)=3.5
B(1,1)=2.0; B(2,1)=5.0
C=MATMUL(A,B) ! it is ok
x=MATMUL(A,B) ! it is wrong
x=C(1,1) ! it is ok <--- exists a better way ??
!
END PROGRAM testing
You have noticed that it is not possible to do intrinsic assignment of an array to a scalar (and C is a rank-2 array of size 1). x=C(1,1) is the correct way to do such assignment from the single element of C to the scalar x.
There are other ways to abstract that correct assignment statement, but probably little value in doing so.
In your specific case, however, there is alternative. Rather than matmul, consider dot_product.
x = DOT_PRODUCT(A(1,:), B(:,1)) ! Scalar result, intrinsic assignment allowed.
per my comment, you can write a very simple function to extract the first element of an array:
real function first(matrix) !return the (1,1,1,..) element of an array
real, intent(in) :: matrix(*)
first=matrix(1)
end function
simply use as:
real :: a(1,2),b(2,1),x
...
x=first(matmul(a,b))
note if you want to make sure this is only used for a dimension(1,1) array you need to use an explicit interface and do:
real function first(matrix)
real, intent(in) :: matrix(:,:)
if(.not.all(shape(matrix).eq.[1,1]))reporterror()
first=matrix(1,1)
end function

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.