I'm writing some subroutines in Fortran90 to perform some numerical computations. However, as part of this, I need to include some codes from the netlib templates library that are written in Fortran77. I'm having a hard time getting them to work - specifically understanding the usage of arrays.
For instance, I need to use a subroutine called GMRES. Here are the arguments:
SUBROUTINE GMRES( N, B, X, RESTRT, WORK, LDW, WORK2, LDW2,
$ ITER, RESID, MATVEC, PSOLVE, INFO )
* .. Scalar Arguments ..
INTEGER N, RESTRT, LDW, LDW2, ITER, INFO
DOUBLE PRECISION RESID
* ..
* .. Array Arguments ..
DOUBLE PRECISION B( * ), X( * ), WORK( * ), WORK2( * )
* ..
* .. Function Arguments ..
*
EXTERNAL MATVEC, PSOLVE
*
*
* Arguments
* =========
*
* N (input) INTEGER.
* On entry, the dimension of the matrix.
* Unchanged on exit.
*
* B (input) DOUBLE PRECISION array, dimension N.
* On entry, right hand side vector B.
* Unchanged on exit.
*
* X (input/output) DOUBLE PRECISION array, dimension N.
* On input, the initial guess; on exit, the iterated solution.
*
* RESTRT (input) INTEGER
* Restart parameter, <= N. This parameter controls the amount
* of memory required for matrix WORK2.
*
* WORK (workspace) DOUBLE PRECISION array, dimension (LDW,5).
* Note that if the initial guess is the zero vector, then
* storing the initial residual is not necessary.
*
* LDW (input) INTEGER
* The leading dimension of the array WORK. LDW >= max(1,N).
*
* WORK2 (workspace) DOUBLE PRECISION array, dimension (LDW2,2*RESTRT+2).
* This workspace is used for constructing and storing the
* upper Hessenberg matrix. The two extra columns are used to
* store the Givens rotation matrices.
*
* LDW2 (input) INTEGER
* The leading dimension of the array WORK2.
* LDW2 >= max(1,RESTRT).
*
* ITER (input/output) INTEGER
* On input, the maximum iterations to be performed.
* On output, actual number of iterations performed.
*
* RESID (input/output) DOUBLE PRECISION
* On input, the allowable error tolerance.
* On ouput, the norm of the residual vector if solution
* approximated to tolerance, otherwise reset to input
* tolerance.
*
* INFO (output) INTEGER
* = 0: successful exit
* = 1: maximum number of iterations performed;
* convergence not achieved.
*
* MATVEC (external subroutine)
* The user must provide a subroutine to perform the
* matrix-vector product A*x = y.
* Vector x must remain unchanged. The solution is
* over-written on vector y.
*
* The call is:
*
* CALL MATVEC( X, Y )
Notice how the arrays are defined WORK( * ), WORK2( * ). So to my mind these are 1D arrays of arbitrary length. But then in the argument description, it seems to be suggesting that they are 2D arrays, matrices, with dimension WORK(LDW, 5). So are they 1D or 2D?
Also, in the GMRES algorithm, these WORK arrays are used like this:
CALL MATVEC(SCLR1, WORK(NDX1), SCLR2, WORK(NDX2))
So if the WORK arrays are 2D, wouldn't the above give a rank mismatch? What does it mean to access a 2D array with just one index like that? Should I define the WORK arrays as 2D or 1D?
Edit
The GMRES routine requires a matvec routine to be supplied. In the GMRES code it is being called like
CALL MATVEC(SCLR1, X, SCLR2, WORK(NDX2))
and also like
CALL MATVEC(SCLR1, WORK(NDX1), SCLR2, WORK(NDX2))
My subroutine MATVEC that I'm trying to supply looks like:
subroutine matvec(alpha, x, beta, y)
real(dp), intent(in) :: alpha, beta
real(dp), dimension(*), intent(in) :: x
real(dp), dimension(*), intent(inout) :: y
real(dp), dimension(*,*) :: A
integer :: n
call make_Jac(n,A)
call dgemv('notranspose', n, n, alpha, A, n, x, 1, beta, y, 1)
end subroutine matvec
Where make_Jac returns my matrix for the problem I'm working on, along with its dimension n. The blas routine dgemv then handles the matrix-vector product.
WORK( * ) declares an assumed size array, which can be two-dimensional. See here.
The compiler will not complain if you feed a one-dimensional array to the subroutine, but weird things (up to and including a segmentation fault) might happen.
Better use arrays matching the specifications.
The same Fortran array can be managed as one dimensional, two dimensional, etc. It is stored in contiguous memory in any case.
Let's say you have
double precision x(3, 2)
call somefunc (x)
This can be accessed, inside somefunc, as y (6).
The array elements are stored in "column major order" which means
x(1, 1) is y(1)
x(2, 1) is y(2)
x(3, 1) is y(3)
x(1, 2) is y(4)
x(2, 2) is y(5)
x(3, 2) is y(6)
As long as the function knows the limits of each dimension, it can calculate linear access by simple arithmetic. Alas, this "relaxed type" is also a frequent source of bugs.
Further to the answer by wallyk the dummy argument work(*) is a rank-1 assumed size array. With such an array
The rank and extents may differ for the effective and dummy arguments; only the size of the effective argument is assumed by the dummy argument.
This means that it is quite acceptable for such a structure as
double precision work(LDW,5)
call GMRES(..., work, ...)
! ...
end
subroutine GMRES(..., work, ...)
double precision work(*)
! ...
end subroutine
Indeed, with the dummy argument as a rank-1 array it isn't allowed to reference it as a rank-2 array. A rank-2 assumed size array would look something like
double precision work(LDW,*)
where then, of course, work(ndx1) would be bad.
Coming to the comment by roygvib, later on in the Netlib source code there is the line
call GMRESREVCOM(..., work, ...)
where that subroutine has the dummy argument
double precision work(LDW,*)
There is probably, then, an expectation that the user of the code will provide initially a rank-2 actual argument.
What all of this means is that it doesn't matter what rank the actual argument passed to work in GMRES as long as it has at least LDW*5 elements. I'd be careful calling the dummy argument as being of "arbitrary length", though, as referencing work(LDW*5+1) (according to my first example) would be wrong. The size of the dummy array is exactly the size of the passed array.
The later call to matvec is not troublesome for yet another reason. This subroutine has four arguments, the first and third of which are scalar. The second and fourth are again assumed-size arrays of rank-1. We've established that assumed-size arrays don't care about the rank of the effective/actual argument, but you're likely wondering why you can pass the scalar argument work(ndx1) to this rank-1 array.
The answer to that is something called sequence association. This means that when your actual argument is an array element designator and the dummy argument is an array dummy argument then that dummy argument is argument associated with a sequence of elements from the actual argument, starting with the element element designated.
That is, you have a rank-1 array like [work(ndx1), work(ndx1+1), ...] as your array x in matvec.
This is all fine, as long as you don't attempt to reference beyond the extent of your actual argument.
Related
I've written a program that calculates the Discrete Fourier Transform of a sample, where in this case I'm sampling a sine wave. To test it, I need to plot the result. However, the resultant array is filled with complex values.
So how do I extract the real and imaginary components of these array elements, and then plot them against their indexes?
Here's my code:
program DFT
implicit none
integer :: k, N, x, y, j, r, l, istat
integer, parameter :: dp = selected_real_kind(15,300)
real, allocatable,dimension(:) :: h
complex, allocatable, dimension(:) :: rst
complex, dimension(:,:), allocatable :: W
real(kind=dp) :: pi, z, P, A, i
pi = 3.14159265359
P = 2*pi
A = 1
!open file to write results to
open(unit=100, file="dft.dat", status='replace')
N = 10
!allocate arrays as length N, apart from W (NxN)
allocate(h(N))
allocate(rst(N))
allocate(W(-N/2:N/2,1:N))
pi = 3.14159265359
!loop to fill the sample containing array
do k=1,N
h(k) = sin((2*k*pi)/N)
end do
!loop to fill the product matrix with values
do j = -N/2,N/2
do k = 1, N
W(j,k) = EXP((2.0_dp*pi*cmplx(0.0_dp,1.0_dp)*j*k)/N)
end do
end do
!use of matmul command to multiply matrices
rst = matmul(W,h)
!print *, h, w
write(100,*) rst
end program
Thanks.
The REAL intrinsic function returns the real part of a complex number in Fortran. It is an elemental function as well, so for an array of type complex simply REAL( array ) will return a real array with the same kind as the original containing the results you want.
The AIMAG intrinsic function returns the imaginary part of a complex number in Fortran. It is an elemental function as well, so for an array of type complex simply AIMAG( array ) will return a real array with the same kind as the original containing the results you want.
Alternatively in Fortran 2003 latter %re and %im can be used to access the real and imaginary part respectively of a complex variable. The comments about their elemental nature again apply.
These are easily found by googling, or better I think every Fortran programmer should at least have access to a copy of Metcalf, Reid and Cohen "Modern Fortran Explained".
in C programming if an 2-D array is given like ( int a[5][3]) and base address and address of particular element (cell ) is also given and have to find index no. of that element(cell) (row and col no.) can we find that? if yes how?
i know the formula of finding address is like this
int a[R][C];
address(a[i][j])=ba+size(C*i+ j);
if ba, R,C,Size and address(a[i][j]) is given... how to find value of i and j?
for finding the value of 2 variable we need 2 equation ..but im not able to find 2nd equation.
The specific address minus the base address gives you the size in bytes, from the base to the specific address.
If you divide that size in bytes with sizeof(ba[0][0]) (or sizeof(int)), you get the number of items.
items / C gives you the first dimension and items % C gives you the second dimension.
Thus:
int ba[R][C];
uintptr_t address = (uintptr_t)&ba[3][2]; // some random item
size_t items = (address - (uintptr_t)ba) / sizeof(ba[0][0]);
size_t i = items / C;
size_t j = items % C;
It is important to carry out the arithmetic with some type that has well-defined behavior, therefore uintptr_t.
If I had done int* address then address - ba would be nonsense, since ba decays into an array pointer of type int(*)[3]. They aren't compatible types.
Use integer division and remainder operators.
If you have the base and a pointer to an element, elt, then there are two things:
In "pure math" terms, you'll have to divide by the size of the elements in the array.
In "C" terms, when you subtract pointers this division is performed for you.
For example:
int a[2];
ptrdiff_t a0 = (ptrdiff_t)&a[0];
ptrdiff_t a1 = (ptrdiff_t)&a[1];
a1 - a0; // likely 4 or 8.
This will likely be 4 or 8 because that's the likely size of int on whatever machine you're using, and because we performed a "pure math" subtraction of two numbers.
But if you let C get involved, it tries to do the math for you:
int a[2];
int * a0 = &a[0];
int * a1 = &a[1];
a1 - a0; // 1
Because C knows the type, and because it's the law, the subtracted numbers get divided by the size of the type automatically, converting the pointer difference into an array-like index or offset.
This is important because it will affect how you do the math.
Now, if you know that the address of elt is base + SIZE * (R * i + j) you can find the answer with integer division (which may be performed automatically for you), subtraction, more integer division, and either modulus or multiply&subtract:
offset or number = elt - base. This will either give you an index (C style) or a numeric (pure math) difference, depending on how you do the computation.
offset = number / SIZE. This will finish the job, if you need it.
i = offset / R. Integer division here - just throw away the remainder.
j = offset - (i*R) OR j = offset % R. Pick what operation you want to use: multiply & subtract, or modulus.
In Matlab I have an array v of length m, a matrix of order n and a function F that takes as an input a single matrix and outputs a number. Starting from v I would like to apply the function to the whole array of matrices whose i-th element consists of a matrix M_i whose entries are obtained by multiplicating all the entries of M by v_i. The output would be itself an array of length n.
As far as I can see there are two ways of achieving this:
Looping on all i=1:n, computing F on all the M_is and store all the corresponding values in an array
Defining a 3-array structure that contains all the matrices M_i and correspondingly extending the function F as to act on 3-arrays instead of matrices. However this entails overloading some matrix operators and functions (transpose, exponential, logarithm, square root, inverse etc...) as to formally handle a 3-array.
I have done the simpler option 1. It takes a long time to execute. Number 2 promises to be faster- However, I am not sure if this is the case, and I am not familiar with overloading operators on Matlab. In particular: how to extend a matrix operator to a 3-array in such a way that it performs the related function on all of its entries.
A for loop is probably no slower than vectorising this, especially for larger problems where memory starts to limit speed. Nevertheless, here are two ways of doing it:
M=rand(3,3,5) % I'm using a 3x3x5 matrix
v=1:5
F=#sum % This is the function
M2=bsxfun(#times,M,permute(v.',[2 3 1])) % Multiply the M(:,:,i) matrix by v(i)
R=arrayfun(#(t) F(M2(:,:,t)),(1:size(M,3)).','UniformOutput',false) % applies the function F to the resulting matrices
cell2mat(R) % Convert from cell array to matrix, since my F function returns row vectors
R2=zeros(size(M,3),size(M,1)); % Initialise R2
for t=1:size(M,3)
R2(t,:)=F(M(:,:,t)*v(t)); % Apply F to M(:,:,i)*v(i)
end
R2
You should do some testing to see which will be more efficient for your actual problem. The vectorised version should be faster for small problems, but use more memory, whereas the for loop will be slower for small problems but use less memory, and so could be faster on larger problems.
I'm working on a project where I have a number of arrays of the same size in the 1st, 2nd and 3rd dimension, although the sizes may vary in the 4th dimension.
I would like to group these arrays by constructing a pointer which concatenates these arrays.
To make this less abstract, let's say I have 2 arrays:
A (size: N1 x N2 x N3 x N4a)
B (size: N1 x N2 x N3 x N4b)
in previous versions of the project these arrays where copied to an array C of size N1 x N2 x N3 x (N4a + N4b) which would then be passed to a subroutine to perform ffts on this array.
I would like to avoid this copying operation and construct a pointer p which would contain the same data as the array C in the previous version but without the explicit copying and additional memory allocation.
Is this possible in Fortran?
No. A pointer cannot point across two otherwise independent objects like that at the same time.
Depending on your situation, what might be workable is to start with an array that is of dimension (N1,N2,N3,N4a+N4b) and then make A and B associated (pointer, storage or argument) to the relevant parts of that initial big array in some way.
REAL, TARGET :: c(N1,N2,N3,N4a+N4b)
REAL, POINTER :: a(:,:,:,:)
REAL, POINTER :: b(:,:,:,:)
a => c(:,:,:,:n4a)
b => c(:,:,:,n4a+1:)
! Go forth and do things with a and b.
! Then later do things with c.
In the dark times, before Fortran had dynamic memory allocation of any sort, this sort of one-array-to-rule-them-all that then got divvied out was in common usage.
How can one combine bunch of large allocatable vectors in an array in Fortran? I'd like to avoid copying and reshaping using reshape as the arrays are large. The effect I want to achieve is just like Fortran's equivalence, to illustrate:
program test_equiv
integer x(10), y(10), z(10), xyz(10,3)
equivalence (x, xyz(1,1))
equivalence (y, xyz(1,2))
equivalence (z, xyz(1,3))
x = 1
y = 2
z = 3
! and we can use just normal array syntax
print *, xyz(3,:)
end program
This will, however, not work with allocatable arrays. If it's about accessing vectors of matrix it can be easily implemented via pointers. But how can one combine vectors in a two dimensional array? Till now I came only to array of pointers which has problems:
program test_arofpntrs
implicit none
integer :: i
integer, allocatable, target :: xs(:), ys(:), zs(:)
type t_p_xs
integer, pointer :: p_xs(:)
end type t_p_xs
type(t_p_xs), allocatable :: coords(:)
allocate(coords(3), xs(10), ys(10), zs(10))
xs = 1
ys = 2
zs = 3
coords(1) % p_xs => xs
coords(2) % p_xs => ys
coords(3) % p_xs => zs
print *, coords(1) % p_xs(:)
! this fails:
!print *, coords(:) % p_xs(1)
end program
This is ugly and one cannot access xs(i), ys(i), zs(i). Is it possible to do what I want without copies?
This will not be possible, if you start with separate 1D arrays. The allocatable arrays may be anywhere in the memory. Although Fortran arrays don't have to be contiguous, there has to be some system of strides.
! this fails:
!print *, coords(:) % p_xs(1)
is prohibited by the Fortran standard, because one can not calculate the address of the next element simply. The 1D arrays are even not guaranteed to have the same length.
Also reshape as such does not have to be inefficient, it may just syntactically help with indexing, but don't touch the data at all.
Pointers are a great tool and may help here. You would have to use your 1D arrays bit differently. For example allocate a long 1D array and have 1D pointers for parts of it and a 2D pointer as a whole, or better the other way round:
real,allocatable,target :: xyz(:,:)
real,pointer :: x(:),y(:),z(:)
allocate(xyz(1:10,1:3))
x => xyz(:,1)
y => xyz(:,2)
z => xyz(:,3)
Even the other order of indexes is possible, i.e., xyz(3,10); x => xyz(1,:).
Also you can do
long1D(1:size(xyz)) => xyz
but note it is a Fortran 2008 feature in this direction (2003 otherwise).