I am currently trying to use the PRIMME library in a code written in Fortran90 (and a bit of Fortran03). PRIMME itself is written in C, but it comes with a Fortran77 interface that should make using it from Fortran relatively easy. I have run into a question though that is related to bringing the different Fortran styles together, to which I have not found a convincing answer yet:
PRIMME is an iterative eigensolver library that calculates the eigenvectors and eigenvalues of huge, symmetric/hermitian matrix A without ever storing the matrix itself in memory. The matrix A is only used in a matrix-matrix multiplication that is implemented outside of the PRIMME library. A function pointer to the matrix-matrix multiplication routine is then passed into the PRIMME library which will then internally call it a number of times to calculate eigenvectors and eigenvalues.
Looking at the Fortran77 binding example that comes with the library, the matrix-matrix multiplication routine should have the following signature.
subroutine MatMatMul(B,C,k,primme)
real*8 B(*), C(*)
integer k, primme
! calculate C = A * B
end
Here k is the number of columns of the matrices B and C. primme is passed as a Fortran integer, but it is actually a C pointer to a datastructure that contains the configuration of the PRIMME library, most notably the dimension of the eigenvalue problem, which is the number of rows of B and C. So within the MatMatMul method I have all the information I need about the sizes of the involved matrices.
Unfortunately the matrices B and C are passed into MatMatMul as one-dimensional assumed size arrays B(*) and C(*). While I could of course do all the index arithmetic myself, I would prefer to access B and C as two-dimensional arrays. Also it would be nice to use two-dimensional slicing on them. The rest of the Fortran codebase is written using modules/explicit interfaces and assumed shape arrays, so this would also be more consistent with the rest of the code.
Is there any way to treat B and C as two-dimensional Fortan arrays with runtime determined sizes?
Given the fact that I know the number of rows and columns at runtime, I thought it should not be a big problem. I searched the internet but did not find a solution (or any discussion of the problem actually). I've read a lot of times that what you can do with these assumed size arrays is very limited, as the compiler does not have enough information about them to do for example slicing. I don't really understand this point, as the shape of all my other allocatable arrays that I pass around as assumed shape arrays is determined at runtime, so the compiler has no information about them anyway but still allows me to do slicing with them. In view of this fact there should be absolutely no problem in treating B(*) as B(n,k).
I also talked to my collegues who have more experience with Fortran, and while they suggested some workarounds, e.g. making MatMatMul a wrapper to another method that accepts B and C as explicit shape two-dimensional arrays and putting the new method outside of a module (so that the interface is not checked by the compiler), they did not have the simple solution I was hoping for.
I do not have too much experience with Fortran yet, so I thought I should ask here to make sure I'm not missing something.
EDIT: Based on the accepted answer below, I went for the following solution:
subroutine WrapperMatMatMul(B,C,k,primme)
real*8 B(*), C(*)
integer k, primme
call primme_get_member_f77(primme, PRIMMEF77_n, n)
call MatMatMul(B,C,k,n,primme)
end
subroutine MatMatMul(B,C,k,n,primme)
real*8 B(k,n), C(k,n)
integer k, n, primme
! calculate C = A * B
end
The pointer to the wrapper is then passed into PRIMME. It's not exactly pretty, but works perfectly.
It is allowed to pass two dimensional arrays thanks to the rules of sequence association. The arrays are re-interpreted as on dimensional in the subroutine in the memory storage order (column major).
See the Fortran standard, or for more informal introduction http://michaelgoerz.net/blog/2011/05/advanced-array-passing-in-fortran/
The only place where it is a problem is generating generic interfaces, where the ranks are strictly enforced in generic disambiguation.
subroutine MatMatMul(B,C,k,primme)
real*8 B(*), C(*)
integer k, primme
! calculate C = A * B
end
real*8 A(10,20), B(20,10)
call MatMatMul(A,B,20,0)
end
or even
real*8 A(10,20), B(20,10)
call MatMatMul(A,B,20,0)
contains
subroutine MatMatMul(B,C,k,primme)
real*8 B(*), C(*)
integer k, primme
! calculate C = A * B
end
end
But I would prefer to use the Fortran 90 intrinsic function MATMUL instead. Or BLAS subroutine dgemv, if you need top performance, but some compilers call it from matmul automatically.
Related
Is it possible in modern Fortran to use a vector to index a multidimensional array? That is, given, say,
integer, dimension(3) :: index = [4,6,9]
double precision, dimension(10,10,10) :: data
is there a better (more general) way to access data(4,6,9) than writing data(index(1), index(2), index(3))? It would be good not to have to hard-code the rank of the data array.
(Naively I would like to write data(index) but of course this actually means something different - subset "gathering" - requiring data to be a rank-one array itself.)
For what it's worth this is essentially the same question as multidimensional index by array of indices in JavaScript, but in Fortran instead. Unfortunately the clever answers there won't work with predefined array ranks.
No. And all the workarounds I can think of are ghastly hacks, you're better off writing a function to take data and index as arguments and spit out the element(s) you want.
You might, however, be able to use modern Fortran's capabilities for array rank remapping to do exactly the opposite, which might satisfy your wish to play fast-and-loose with array ranks.
Given the declaration
double precision, dimension(1000), target :: data
you can define a rank-3 pointer
double precision, pointer :: index_3d(:,:,:)
and then set it like this:
index_3d(1:10,1:10,1:10) => data
and hey presto, you can now use both rank-3 and rank-1 indices into data, which is close to what you want to do. I've not used this in anger yet, but a couple of simple tests haven't revealed any serious problems.
This code fragment:
real*8 a(20,5,2)
real*8 b(5)
real*8 c(20,5,2)
! define a vals ....
! define b vals ....
c(1:20, :, 1:2) = a(1:20,:,1:2)*b
won't compile because b doesn't have the same shape as a or c. I, of course, want the five values of b to match to the 5 values of the middle indices of a and c, but the Fortran compiler doesn't understand this. Is there some way to tell it what I want here? I know I could replicate b in a larger array to match the shape of a and c but that wastes memory. I could also put the whole thing in loops but for the actual code I am trying to write that will be cumbersome. Are there any other possibilities?
I think that Fortran is either preventing you shooting yourself in the foot, or, if you prefer, insisting that you be clear about what array elements you want to multiply. The expression
a(1:20,:,1:2)
is a section of 40 elements for each value of the 2nd index. It's not at all clear what the 5 elements of b are to multiply.
I suspect that you are looking for the spread function which is the one to use to 'uprank' an array. Without clarification it's kind of difficult to propose an appropriate application of spread, perhaps if you explain further you'll get a better answer than this one.
Heck, let's go ahead without clarification ...
I interpret OP's intention to be to compute the elements of c thusly:
DO ix = 1,5
c(:,ix,:) = a(:,ix,:)*b(ix)
END DO
which can be replaced by
c = a * SPREAD(SPREAD(b,dim=1,ncopies=20),dim=3,ncopies=2)
I've given this only very limited testing, the lesson is perhaps to follow #ptb's advice to stick to do loops.
I have to write a subroutine in Fortran 77(i'm using Intel Fortran), which reads the measured values from a text file and stores them in a matrix.
Since the number of measured values is always variable, I must dynamically allocate the matrix.
I know that the dynamic allocation is only possible from Fortran 90, but at that time people had the same problems, so it is also possible.
How would you proceed?
I do not want to reserve too much space for the matrix because the method is impractical for me.
If you really are restricted to Fortran 77, you do not do dynamic allocation. Instead, declare an array that is larger than what you think you will likely need, without it being too large to prevent the program from running on your target system. Then store your values in that large array, separately keeping track of how many elements of the large array that you use. If your choice of array size was not large enough, let the user know and terminate the program.
People found the lack of dynamic allocation in Fortran 77 very restrictive, so they often resorted to using non-standard language extensions. If you decide to go down the path of language extensions, then these days the best extension to Fortran 77 to use in this situation is the allocatable array feature introduced with Fortran 90. I think it is fair to say that all actively maintained compilers that can handle Fortran 77 will also handle Fortran 90 allocatable arrays (and then some).
As many people have pointed out, you don't have to stick to Fortran77, even if much of what is already written is Fortran77 compatible. Even the few features that have been deleted in Fortran 95 See Wikipedia for a list, your compiler will probably still work fine, as long as you don't switch from Fixed Form to Free Form in the same file.
Pre-F90, what people would probably do is to declare arrays that are (hoped to be) big enough for any use case, then only use the first elements of that array.
One thing that I am not certain about, but which might be useful, is the change of scope. Short example:
subroutine main(n)
implicit none
integer n
integer a(n)
print*, "Please enter the ", n, " numbers"
read*, a
print*, "Sum is ", sum(a)
end subroutine main
program dynamic
implicit none
integer n
print*, "Enter size of array:"
read*, n
call main(n)
end program dynamic
I'm curious to know whether this would be Fortran77 compliant. I honestly don't know. #francescalus has convinced me that it isn't.
I need to perform both single precision and double precision arithmetics on a variable in different parts of my code. So basically, I declare the variable as single precision first. Then I call subroutine sub_a which makes use of double precision version of the variable and performs double precision operations on that:
program main
implicit none
integer,parameter :: single = selected_real_kind(p=6,r=37)
integer,parameter :: double = selected_real_kind(p=15,r=307)
real(single),allocatable,dimension(:) :: A
real(double),allocatable,dimension(:) :: B
allocate(A(3),B(3))
A=2 ! single precision
A=A+3 ! single precision
print '(a,1x,3(f20.15))','sqrt(A),single:',sqrt(A)
print '(a,1x,I15)','mem_address of A before sub_a:',loc(A)
call sub_a(real(A,kind=double),B) ! double precision
print '(a,1x,3(f20.15))','sqrt(A),double:',B
contains
subroutine sub_a(a,b)
real(double),dimension(:),intent(in) :: a
real(double),dimension(:),intent(inout) :: b
print '(a,1x,I15)','mem_address of A in sub_a:',loc(a)
b=sqrt(a)
end subroutine sub_a
end program main
As seen in the code, I also obtained the memory address of A prior to calling sub_a and the version of A inside sub_a and they are expectedly different.
My questions are:
Is the version of A inside the sub_a allocated in the heap memory so I should not be worried about the size limitation?
is there any potential issue/bug in writing this example?
is there a better way of capturing the purpose described in this example, specially for larger size arrays?
Many Thanks
Update:
I haven't experienced any Memory issue for very large arrays, when using gfortran4.6 / ifort13.1 as compiler.
I plan to use suggestion by #innoSPG as an alternative approach.
by the nature of the call, the version of A that you have in sub_a is a temporary array created by a piece of code included by the compiler. However, if you will manipulate very large arrays, it is not a good idea.
For question 2. to my knowledge, there is no bug. The only issue is the temporary array that may be a problem if you have large arrays and limited memory on your system.
For question 3. In the case there is a memory issue, you can write sub_a to accept simple precision, and then convert each element in sub_a before using it in computation.
The temporary array at the call site can be made at the stack or at the heap, it is implementation dependent. Compilers usually have options to control the behavior.
With Intel Fortran it the option -heap-arrays n (n cannot be ommited or too low, because performance would be bad).
Gfortran would put them on the heap automatically if it doesn't know the size beforehand. It is good to use -fstack-arrays for better performance (it is included in -Ofast).
You will find similar options in other compiler's documentation.
I would not fear and avoid the temporary arrays at all cost. The possibility of much shorter (and more readable!) code is sometimes more important. Many codes have portions that are done only once during initialization or final output where the performance penalty is often irrelevant. I personally use it to make my code cleaner in these parts (not in the computational core).
Is it possible in modern Fortran to use a vector to index a multidimensional array? That is, given, say,
integer, dimension(3) :: index = [4,6,9]
double precision, dimension(10,10,10) :: data
is there a better (more general) way to access data(4,6,9) than writing data(index(1), index(2), index(3))? It would be good not to have to hard-code the rank of the data array.
(Naively I would like to write data(index) but of course this actually means something different - subset "gathering" - requiring data to be a rank-one array itself.)
For what it's worth this is essentially the same question as multidimensional index by array of indices in JavaScript, but in Fortran instead. Unfortunately the clever answers there won't work with predefined array ranks.
No. And all the workarounds I can think of are ghastly hacks, you're better off writing a function to take data and index as arguments and spit out the element(s) you want.
You might, however, be able to use modern Fortran's capabilities for array rank remapping to do exactly the opposite, which might satisfy your wish to play fast-and-loose with array ranks.
Given the declaration
double precision, dimension(1000), target :: data
you can define a rank-3 pointer
double precision, pointer :: index_3d(:,:,:)
and then set it like this:
index_3d(1:10,1:10,1:10) => data
and hey presto, you can now use both rank-3 and rank-1 indices into data, which is close to what you want to do. I've not used this in anger yet, but a couple of simple tests haven't revealed any serious problems.