Array section arithmetic in Fortran90 - arrays

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.

Related

Is there a way to use an array as an address for another array in Fortran [duplicate]

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.

Copying arrays: loops vs. array operations

I work with Fortran now quite a long time but I have a question to which I can't find a satisfying answer.
If I have two arrays and I want to copy one into the other:
real,dimension(0:100,0:100) :: array1,array2
...
do i=0,100
do j=0,100
array1(i,j) = array2(i,j)
enddo
enddo
But I also noticed that it works as well if I do it like that:
real,dimension(0:100,0:100) :: array1,array2
...
array1 = array2
And there is a huge difference in computational time! (The second one is much faster!)
If I do it without a loop can there be a problem because I don't know maybe I'm not coping the content just the memory reference?
Does it change anything if I do another mathematical step like:
array1 = array2*5
Is there a problem on a different architecture (cluster server) or on a different compiler (gfortran, ifort)?
I have to perform various computational steps on huge amounts of data so the computational time is an issue.
Everything that #Alexander_Vogt said, but also:
do i=0,100
do j=0,100
array1(i,j) = array2(i,j)
enddo
enddo
will always be slower than
do j=0,100
do i=0,100
array1(i,j) = array2(i,j)
enddo
enddo
(Unless the compiler notices it and reorders the loops.)
In Fortran, the first parameter is the fastest changing. That means that in the second loop, the compiler can load several elements of the array in one big swoop in the lower level caches to do operations on.
If you have multidimensional loops, always have the innermost loop loop over the first index, and so on. (If possible in any way.)
Fortran is very capable of performing vector operations. Both
array1 = array2
and
array1 = array2*5
are valid operations.
This notation allows the compiler to efficiently parallelize (and/or) optimize the code, as no dependence on the order of the operations exists.
However, these construct are equivalent to the explicit loops, and it depends on the compiler which one will be faster.
Whether the memory will be copied or not depends on what further is done with the arrays and whether the compiler can optimize that. If there is no performance gain, it is safe to assume the array will be copied.

Efficient way to see if a set of values appear in an array?

I'm trying to check if an 1D array of integers A contains or not, at every one of it's size(A) positions, any of the elements of the set of integers S (also a 1D array), with the general case of size(S) > 1.
The easy and obvious way is to do the following nested loop:
DO i = 1, size(A)
DO j = 1, size(S)
IF(A(i) == S(j)) ** do something **
ENDDO
ENDDO
The problem is that, for large arrays A and S, this process is very inefficient. Is there an intrinsic FORTRAN subroutine or function that does this faster? Or any other method?
I've tried to do the following, but it doesn't want to compile:
DO i = 1, NNODES
IF(A(i) == ANY(S)) ** do something **
ENDDO
The error message that appears is the following: "error #6362: The data types of the argument(s) are invalid." I'm using VS2010 with Intel Parallel Studio 2013.
The expression
A(i) == ANY(S)
has an integer on the lhs and a logical on the rhs. We'll have none of that C-inspired nonsense of regarding those as comparable types in Fortran thank you very much. Actually, it's worse than that, any returns a logical but takes an array of logicals on input, so any(array_of_int) won't compile.
You could try
ANY(S==A(i))
instead. That should give you a compilable solution.
Now, as for efficiency, you're first snippet is O(n^2). You can do better, asymptotically. Sort both arrays and scan them in tandem, which is O(n + n log n) or something similar. If you need help coding that up, update your question, though I suspect it's already been asked and answered here on SO.
I strongly suspect, and you can check if you care to, that using any inside a single (explicit) loop will also be O(n^2) -- since any has to operate on the most general cases I can't see any realistic alternative to it scanning the array -- another loop in other words.
In addition to High Performance Mark's answere, when you scan the sorted arrays you can, and should, use a binary search algorithm.

Reinterpretation of assumed size array

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.

Using a vector to index a multidimensional Fortran array

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.

Resources