Related
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
I want to manipulate a large code. I want to calculate rmse of the result calculated in the main program. I have added following subroutine to calculate rmse
subroutine rmse(A,B,nmax,rmseans)
Integer nmax
Double Precision A(nmax),B(nmax),C(nmax),d(nmax)
Real e,f,rmseans
COMMON /PAR7/ XTIME,STAGE,RECH,OBS
e = 0
do 103 i=1,nmax
c(i)= (a(i)-b(i))
d(i)= (c(i))**2
e = e+d(i)
103 enddo
rmseans = sqrt(e/nmax)
End`
It takes two arrays A and B and gives rmse as rmseans while nmax is total number of terms.
Then I am calling at end of main program this subroutine as
Call rmse(OBS,H,NT,rmseans)
Write(*,*) rmseans
But this is giving me quite wrong answer. I have made sure that values OBS,and H are calculated before calling the subroutine by writing these two values in a file.
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.
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?
i have allocated a lot of 2D arrays in my code, and I want each one array to read from a file named as array's name. The problem is that each array has different size, so I am looking for the most efficient way. The code is like this:
Module Test
USE ...
implicit NONE
private
public:: initializeTest, readFile
real(kind=8),dimension(:,:),allocatable,target:: ar1,ar2,ar3,ar4,ar5,...,ar10
real(kind=8),dimension(:,:),pointer:: pAr
CONTAINS
!
subroutine initializeTest
integer:: k1,k2,k3,k4,k5
integer:: ind1,ind2
allocate(ar1(k1,k1),ar2(k1,k2),ar3(k2,k4),ar4(k5,k5),...) !variable sizes
! here needs automatization - since its repeated
pAr => ar1
ind1 = size(pAr,1)
ind2 = size(pAr,2)
call readFile(par,ind1,ind2)
pAr => ar2
ind1 = size(pAr,1)
ind2 = size(pAr,2)
call readFile(par,ind1,ind2)
!....ar3, ... , ar9
pAr => ar10
ind1 = size(pAr,1)
ind2 = size(pAr,2)
call readFile(par,ind1,ind2)
end subroutine initializeTest
!
!
subroutine readFile(ar,row,col)
real(kind=8),dimension(row,col)
integer:: i,j,row,col
! it should open the file with same name as 'ar'
open(unit=111,file='ar.dat')
do i = 1, row
read(222,*) (ar(i,j),j=1,col)
enddo
end subroutine importFile
!
!
end module Test
If your arrays ar1, ar2, etc. had the same dimensions you could put them all in a 3-dimensional array. Since they have different dimensions, you can define a derived type, call it a "matrix", with an allocatable array component and then create an array of that derived type. Then you can read the i'th matrix from a file such as "input_1.txt" for i=1.
The program below, which works with g95 and gfortran, shows how the derived type can be declared and used.
module foo
implicit none
type, public :: matrix
real, allocatable :: xx(:,:)
end type matrix
end module foo
program xfoo
use foo, only: matrix
implicit none
integer, parameter :: nmat = 9
integer :: i
character (len=20) :: fname
type(matrix) :: y(nmat)
do i=1,nmat
allocate(y(i)%xx(i,i))
write (fname,"('input_',i0)") i
! in actual code, read data into y(i)%xx from file fname
y(i)%xx = 0.0
print*,"read from file ",trim(fname)
end do
end program xfoo
As far as I know, extracting the name of the variable from the variable at runtime isn't going to work.
If you need lots of automation for the arrays, consider using an array of a derived type, as one other answer suggests, in order to loop over them both for allocation and reading. Then you can enumerate the files, or store a label with the derived type.
Sticking to specific array names, an alternative is to just read/write the files with the required name as an argument to the routine:
Module Test
...
! here needs automatization - since its repeated
call readFile(ar1,'ar1')
call readFile(ar2,'ar2')
!....ar3, ... , ar9
call readFile(ar10,'ar10')
end subroutine initializeTest
subroutine readFile(ar,label)
real(kind=8) :: ar(:,:)
character(len=*) :: label
integer:: i,j,nrow,ncol,fd
nrow=size(ar,1)
ncol=size(ar,2)
open(newunit=fd,file=label)
do i = 1, row
read(fd,*) (ar(i,j),j=1,col)
enddo
end subroutine readFile
end module Test
Some unsollicited comments: I don't really get why (in this example) readFile is public, why the pointers are needed? Also, kind=8 shouldn't be used (Fortran 90 kind parameter).