Transform array of rank 2 to array of rank 1 - arrays

I have the following minimal example:
program main
double precision, dimension(3) :: rankone
double precision, dimension(3,1) :: ranktwo
double precision, dimension(3) :: output
rankone = 1
ranktwo = 2
output = rankone + ranktwo
print *, 'output: ', output
end program main
Is there a function like squeeze in Matlab that removes the singleton dimension from the variable ranktwo. I'm looking for something like
output = rankone + squeeze(ranktwo)
If not, is there any workaround for this setting?
Are there any differences between the various versions of Fortran regarding this problem?

Try this
output = rankone + reshape(ranktwo,[3])
then look at the documentation for reshape to see what is going on. You could also write
output = rankone + ranktwo(:,1)
which slices a 1D array out of ranktwo. In my experience reshape usually causes an array copy so there may be a memory-use-efficiency argument for preferring the second version.
No, there are no differences between Fortran versions regarding this problem, a 3x1 array is not the same shape as a 1D array of length 3.

Alternatively, in F2003 you may point to your rank one array with a rank two pointer, like this:
program main
double precision, dimension(3), target :: rankone
double precision, dimension(3,1) :: ranktwo
double precision, dimension(3,1) :: output
double precision, pointer :: pp(:,:)
rankone(:) = 1
ranktwo(:,:) = 2
pp(1:size(rankone),1:1) => rankone
output = pp + ranktwo
print *, 'output: ', output
end program main
The other direction (pointing to a rank two array by a rank one pointer) only works in Fortran 2008, if I remember correctly.

Related

Efficient way to do tensor product and reshape of two arrays with Fortran

I want to do the tensor product of two arrays A and B, of size L x M, and reshape the result AB so that its size is L^2 x M^2.
To me, the easiest way to do it is with this code
abj=0
do aj=1,M
do bj=1,M
abj=abj+1
abi=0
do ai=1,L
do bi=1,L
abi=abi+1
AB(abi,abj)=A(ai,aj)*B(bi,bj)
end do
end do
end do
end do
Is there a more efficient way to accomplish the same task?
EDIT
Two clarifications
Using BLAS/LAPACK is totally fine
Typically, L and M are of the order of hundreds
I'd be inclined to first create an L*L*M*M array, and then to reshape that, something like
integer, parameter :: l = 10
integer, parameter :: m = 20
integer :: a(l,m)
integer :: b(l,m)
integer :: ab(l*l,m*m)
integer :: intermediate(l,l,m,m)
integer :: i,j
do i=1,m
do j=1,l
intermediate(:,j,:,i) = a*b(j,i)
enddo
enddo
ab = reshape(intermediate, [l*l, m*m])
This should run a little faster than the naive quadruple loop, as the matrix*scalar multiplication can be optimised (or indeed performed using BLAS/LAPACK if desired).
The representation in memory of ab and intermediate is identical, so you might want to use associate to avoid the reshape and copy, e.g.
integer, parameter :: l = 10
integer, parameter :: m = 20
integer :: a(l,m)
integer :: b(l,m)
integer :: ab(l*l,m*m)
integer :: i,j,x,y
do i=1,m
x = m*(i-1)
do j=1,l
y = l*(j-1)
associate(temp => ab(y+1:y+l, x+1:x+m))
temp = a*b(j,i)
end associate
enddo
enddo

Access element of N-D array using a vector [duplicate]

This question already has an answer here:
Using a vector to index a multidimensional Fortran array
(1 answer)
Closed 4 years ago.
Is there a way I can access the nth element of an array a, where n is a 1D array and size(n) is the rank of a.
Edit 2015-08-22 15:21
I am thinking of something similar to
program Example1D
integer :: a(6), b(1)
a = reshape( (/ (i , i = 1, size(a) ) /) , shape(a) )
b = (/ 5 /)
write(*,*) a(b)
end program Example1D
So I can call like this
program Want2D
integer :: a(6,5), b(2)
a = reshape( (/ (i , i = 1, size(a) ) /) , shape(a) )
b = (/ 5 , 3 /)
write(*,*) a(b)
end program Want2D
You are attempting to use a vector-subscript (e.g. Fortran 2008, cl. 6.5.3.3.2). This allows you to use a vector (1D array) to select random elements from an array dimension. This, however, cannot be used exactly as you intend it to select an element from multiple dimensions.
From 6.5.3.3 (emphasis mine):
In an array-section having a section-subscript-list, each subscript-triplet and vector-subscript in the section sub-
script list indicates a sequence of subscripts, which may be empty. Each subscript in such a sequence shall be
within the bounds for its dimension unless the sequence is empty. The array section is the set of elements from
the array determined by all possible subscript lists obtainable from the single subscripts or sequences of subscripts
specified by each section subscript.
If you goal in your example code is to select the element a(5,3) with your vector b = [5, 3], then you could change your write from
write (*,*) a(b) ! doesn't work
to:
write (*,*) a(b(1),b(2)) ! does work
You can do more complicated array-sections with b of higher ranks as long as you use a 1D section for each dimension of a. For example if a is a 5x5 array, you could get the corners with of a as a 2x2 array with:
integer :: b(2,2) ! 2 dimensional
b(1,:) = [1,5]
b(2,:) = [1,5]
write (*,*) a(b(1,:),b(2,:)) ! prints a(1,1), a(5,1), a(1,5), a(5,5)
In the comments below you requested that this be abstracted to an n-dimensional array a. Below is a function I consider ugly due to its need to use c interop in a way I consider a hack. You'll also need a newer compiler to even use the code, as it depends on assumed-rank arrays. Here is a module containing a subroutine that takes a 2-dimensional b containing array indices to print and an n-dimensional a to get the values from.
module future
implicit none
contains
subroutine print_array_vector(a, b)
use, intrinsic :: iso_c_binding, only: c_loc, c_f_pointer
implicit none
integer, dimension(..), target :: a
integer, dimension(:,:) :: b
integer :: a_rank, b_len1
integer, dimension(:,:,:), pointer :: a3
integer, dimension(:,:), pointer :: a2
integer, dimension(:), pointer :: a1
a_rank = rank(a)
if (a_rank /= size(b,1)) then
print *, "Rank mismatch between array and vector"
return
end if
if (a_rank == 3) then
call c_f_pointer(c_loc(a), a3, shape=[size(a,1), size(a,2), size(a,3)])
print *, a3(b(1,:),b(2,:),b(3,:))
else if (a_rank == 2) then
call c_f_pointer(c_loc(a), a2, shape=[size(a,1), size(a,2)])
print *, a2(b(1,:),b(2,:))
else if (a_rank == 1) then
call c_f_pointer(c_loc(a), a1, shape=[size(a,1)])
print *, a1(b(1,:))
else
print *, "Unsupported rank"
return
end if
end subroutine print_array_vector
end module future
This takes in assumed-rank a, which is not directly usable in Fortran except to pass as an actual argument to a C interface. However, we can use other parts of c-interop to get the C pointer to a, then turn it into a Fortran pointer of the appropriate shape. Now we have a in a usable form, but we have to do this within if/else blocks properly reference the different cases. I've only implemented up to 3-dimensional a, the rest is left as an exercise to the reader.
To use this function, here is an example:
program test
use future
implicit none
integer :: a3(5,5,5), a2(5,5), a1(5)
integer :: b3(3,2), b2(2,2), b1(1,2)
integer :: i
a3 = reshape([(i,i=1,125)],shape(a3))
a2 = reshape([(i,i=1,25)],shape(a2))
a1 = [(i,i=1,5)]
b3 = reshape([1,1,1,5,5,5],shape(b3))
b2 = reshape([1,1,5,5],shape(b2))
b1 = reshape([1,5],shape(b1))
call print_array_vector(a1,b1)
call print_array_vector(a2,b2)
call print_array_vector(a3,b3)
end program test
This constructs a 3-dim a, a 2-dim a, and a 1-dim a, and a few 2-dim b's with the locations of the corners of the arrays and then we call the function to print the locations of the vector from the array.
% ./arraysection
1 5
1 5 21 25
1 5 21 25 101 105 121 125
I compiled and tested this with gfortran 5.2 and I have no idea the current state of support for assumed-rank arrays in other version of gfortran or in other Fortran compilers.

ifort -> gfortran: array - Incompatible ranks 2 and 1 in assignment at (1)

I'm trying to move below Intel Fortran line to gfortran, but I get following error:
DOUBLE PRECISION, DIMENSION(0:0,0:0) :: value = (/ -999D99 /)
Incompatible ranks 2 and 1 in assignment at (1)
If I understand it correctly, we are creating a 2-dim array with 1 element. I came with following fix. Is this standard conforming?
DOUBLE PRECISION, DIMENSION(0:0,0:0) :: value = reshape ((/-999D99/), shape(value))
It is not allowed to make an assignment (even in initialization) between arrays of different ranks. Therefore the line
...DIMENSION(0:0,0:0) :: value = (/ -999D99 /)
is illegal.
Reshaping the right hand side to an array of rank 2
...DIMENSION(0:0,0:0) :: value = reshape ((/-999D99/), shape(value))
is a standard conforming solution, but it is easier to assign a scalar:
...DIMENSION(0:0,0:0) :: value = -999D99
Of course, this will work only if you have just 1 value. It will be assigned to all elements of the array on the left hand side.

Implementing Laplace expansion in Fortran

I have to write a program that solves linear equations with the Cramer method, and ask specifically to find the determinant with the Laplace expansion.
det A = sum on i=1...N:(-1)**(i+1) a_i1 det ||A||_i1
where ||A||_i1 is the cofactor matrix of A, a n-1 X n-1 matrix, created by eliminating the i row and 1 column.
and that's where I'm stuck.
This is what I wrote so far
integer, parameter :: rk= selected_real_kind(6)
end module prec
module lap
use prec
implicit none
contains
recursive function det(a,n) result (d)
real(kind=rk), intent(in), dimension(n,n) :: a
real(kind=rk), dimension(n-1,n-1) :: b
real(kind=rk) :: d
integer ::i
integer, intent(in)::n
if (size(a) > 4) then
do i=1,n
b(1:(i-1),:) = a(1:(i-1),:)
b(i:n,:) = a((i+1):n,:)
b(:,:) = a(:,2:n)
d= ((-1)**(i+1))*a(i,1)*det(b,n)
end do
else
d = a(1,1)*a(2,2)-a(1,2)*a(2,1)
end if
end function det
end module lap
program sistema
it keeps telling me that I have non-conformant arrays, even though I'm using the subsets (and my professor says it's quite easy to obtain |A| with subsets).
It would be nice to see the portions of your code that are missing. However, I think I see the problem:
With dimensions a(n,n) and b(n-1,n-1), you cannot do b(1:(i-1),:) = a(1:(i-1),:) because the sizes of second dimension do not match. You should check the definition of a cofactor matrix - you should be removing a column as well as a row. Instead of the the three lines setting b, you should try:
b(1:(i-1),:) = a(1:(i-1),2:n)
b(i:n,:) = a((i+1):n,2:n)
Edit: damn, I always mix up row and column. Perhaps I mean remove a row instead of a column?

How to fill bidimensional arrays in fortran90

i have an issue about filling a bidimensional array in Fortran90. in my program I extract different sets of random numbers and check them as uncertainties to my measurements ustar and Tstar, and i get uerr and terr. Now I want to put uerr and terr in a two-dimensional array because then I have to repeat this procedure n times to get different dataset (the Monte Carlo method for the estimation of errors). How do I assign uerr and terr to my array? And how do I iterate the process n times each time you run the program? I tried giving my array "set" the indexes r and c but at compile I get:
Warning: Extension: REAL array index at (1)
i read ustar and tstar from an input file, the code is below:
program stimerr
implicit none
character(len=12) filein,fileout
integer,dimension(1) :: seed = (/4/)
real, dimension(2) :: num
integer :: n,h,i
real, dimension(24,2) :: set
real :: pi = 3.14159
real :: g1,g2,ustar,tstar,uerr,terr
write(*,'(2x,''File di input .......''/)')
read(*,'(a12)') filein
open(unit=120,File=filein)
set = 0.
call init_random_seed ()
do n=1,24
read(120,*) h,ustar,tstar
call random_number(num)
g1 =(sqrt(-2*log(num(1)))*(cos(2*pi*(num(2)))))/10.
g2 =(sqrt(-2*log(num(1)))*(sin(2*pi*(num(2)))))/10.
uerr = ustar + g1
terr = tstar + g2
write(*,*) set(uerr,terr)
enddo
close(120)
end program stimerr

Resources