Index multiple non-adjacent elements of a Fortran array - fortran

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.

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.

Using maxval or minval functions [duplicate]

I would like to access the the elements of an array in a an arrayed derived type using the subroutine sum_real. That is: sum over first entry in the weight for all people.
type my_type
real, dimension(:), allocatable :: weight
real :: total_weight
end type my_type
type (my_type), dimension (:), allocatable :: people
type (my_type) :: answer
allocate (people (2))
allocate (people (1)%weight(2))
allocate (people (2)%weight(2))
people (1) % weight(1) = 1
people (2) % weight(1) = 1
people (1) % weight(2) = 3
people (2) % weight(2) = 3
call sum_real ( people (:) % weight(1), answer % total_weight )
What I want to do is similar to the answer found in Array of derived type: select entry, except for the fact that I have an allocated array in an arrayed derived type instead of an single element.
But, I get a compiler error:
error #7828: The part-name to the right of a part-ref with nonzero rank has the ALLOCATABLE attribute (6.1.2). [WEIGHT]
What you try is not possible if your component is allocatable. The reference (6.1.2) is actually a reference to the official standard documents, which prohibits this.
The reason is simple, the allocatable components (scalar or arrays) are stored in a different part of memory than the derived type itself. Therefore if you write
sum(people%total_weight)
or
people%total_weight = 0
it is no problem, total_weight is not allocatable, it is stored within the derived type and the compiler just goes in a simple loop and sets one field after another to zero. You can know the address of each %totalweight beforehand.
On the other hand
sum(people%weight)
or
people%weight = 0
each %weight is stored elsewhere and you don't have any simple formula to compute where is each %weight(i).
The solution is either, to fix the size of the array, if possible
real, dimension(2) :: weight
or use a do loop
s = 0
do i = 1, size(people)
S = S + sum(people(i)%weight)
end do
If you have a F2003 compiler, and the bounds of the component array are the same for a particular parent array object, a third approach to the size specified by constant expression/use a do loop methods specified by VladimirF is to parameterize the type.
type my_type(n) ! This type has one parameter - n
integer, len :: n ! The parameter n is a length parameter.
real :: weight(n) ! This component depends on that parameter.
end type my_type
type (my_type(:)), dimension(:), allocatable :: people
! This integer is the size of the people component. Because
! people is allocatable it can be determined at runtime.
number_of_people = 2
! This integer is the size of the weight component that we want
! in the array people. Other arrays and scalars of type
! my_type can have different sizes for that component.
! Because people is allocatable this can be determined at
! runtime.
number_of_weights = 2
allocate( my_type(number_of_weights) :: people(number_of_people) )
! Define people%weight here.
people(1)%weight(1) = 1
...
! Using sum intrinsic for the sake of example
do i = 1, people%n
! The argument to sum is an array section.
print *, sum(people%weight(i))
! ^ ^ Reference to an element of a component
! | Reference to the entire people array
end do
Every element in an array of parameterized type has the same type parameters, hence every weight component in people has the same bounds, hence references such as people%weight become "regular".
Code using this approach (or the constant component size specification approach) still has to follow the restriction for component references that only one part of the reference can have non-zero rank (you can't work with people%weight as a whole as both people and the weight component have rank one).
In the allocatable component case some components in some elements might not be allocated and where they are allocated the component might have different bounds, making a "regular" reference to the data in the component across the elements of the array conceptually difficult.

Fortran DO loop over generic set of indices?

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

Declaration of 2D array dimension - first dimension must be the one element from 1D array [duplicate]

I am trying to make an array with different lengths in a second dimension e.g.:
A = 1 3 5 6 9
2 3 2
2 5 8 9
Is this possible? I've spent a fair amount of time looking but cannot find out either way.
Yes and no. First the no:
Proper arrays in Fortran, such as those declared like this:
integer, dimension(3,3,4) :: an_array
or like this
integer, dimension(:,:,:,:), allocatable :: an_array
are regular; for each dimension there is only one extent.
But, if you want to define your own type for a ragged array you can, and it's relatively easy:
type :: vector
integer, dimension(:), allocatable :: elements
end type vector
type :: ragged_array
type(vector), dimension(:), allocatable :: vectors
end type ragged_array
With this sort of approach you can allocate the elements of each of the vectors to a different size. For example:
type(ragged_array) :: ragarr
...
allocate(ragarr%vectors(5))
...
allocate(ragarr%vectors(1)%elements(3))
allocate(ragarr%vectors(2)%elements(4))
allocate(ragarr%vectors(3)%elements(6))
looking at the first answer, it seems there is no need to create the derived type vector which is really just an allocatable integer array:
type ragged_array
integer,allocatable::v(:)
end type ragged_array
type(ragged_array),allocatable::r(:)
allocate(r(3))
allocate(r(1)%v(5))
allocate(r(2)%v(10))
allocate(r(3)%v(15))
this makes the notation a little less cumbersome..

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.