To: J3 J3/18-247
From: Van Snyder
Subject: Supporting rank genericity
Date: 2018-September-13
References: 04-195
Introduction
============
MAXLOC(A) returns a rank-1 array whose extent is equal to the rank of A.
It cannot be used directly as a subscript for A. One must assign it to
a variable of the appropriate rank, and use elements of that array as
subscripts. For example
integer :: S(3)
real :: A(10,10,10)
...
s = maxloc(a)
print *, 'Maximum value in A is ', a(s(1),s(2),s(3))
UBOUND(A) returns a rank-1 array whose extent is equal to the rank of A.
It cannot be used directly as upper bounds for an automatic variable.
For example
real, intent(in) :: A(:,:,:)
real :: X(ubound(a,1),ubound(a,2),ubound(a,3))
or
real, allocatable :: X(:,:,:)
...
allocate ( x(ubound(a,1),ubound(a,2),ubound(a,3)) )
or, if you need a halo around all dimensions of X
allocate ( x(lbound(a,1)-1:ubound(a,1)+1, &
& lbound(a,2)-1:ubound(a,2)+1, &
& lbound(a,3)-1:ubound(a,3)+1) )
These are tedious if you know the rank, and impossible if we develop
facilities for rank-generic programming.
Proposal
========
1. Array bounds
---------------
Allow a single array B of rank one and extent R to specify the upper or
bounds of a rank R array A. The extent of B shall be a constant, except
maybe for the A(..) case. For example
real, intent(in) :: A(:,:,:)
real :: X(ubound(a))
or
real, allocatable :: X(:,:,:)
...
allocate ( x(ubound(a)) )
or (for the halo case)
allocate ( x(lbound(a)-1:ubound(a)+1) )
The case of one bound being a scalar and the other not is obvious:
allocate ( x(0:ubound(a)) )
is the same as
allocate ( x([0,0,0]:ubound(a)) )
Indeed, the default case (above) of lower bounds being all 1
allocate ( x(ubound(a)) )
is the same as
allocate ( x(1:ubound(a)) )
2. Subscripts
-------------
Allow a single array of rank one and extent R as the subscript of a
rank R array. For example
real :: A(10,10,10)
...
print *, 'Maximum value in A is ', a(maxloc(a))
Then A(S) is the same as A(3,4,5), not A(3:3,4:4,5:5).
Let's not do kludges and half measures, which is how we got nonpointer
optional arguments corresponding to disassociated pointers being
absent. The original solution proposed for the combinatorial-explosion
problem was a special case of the conditional expression proposal, with
an empty ELSE part meaning "if the logical expression is true, the
argument is the consequent (not its value), else the argument is
absent."
The obvious generalization here is to allow a single subscript S of rank
K+1 and extents (R,N1, ..., NK) as a subscript for a rank-R array A.
The elements of the rank-one sections in the first dimension of S are
used consecutively as subscripts for A, as in the rank one case,
resulting in a rank K array of extents N1, ..., NK.
This provides a more general scatter/gather facility than the present
vector subscript facility. This is not the same as using the elements
of the rank-one sections in S as vector subscripts for A, which would
result in a rectangular section of shape [n,n] (in the case A is
of rank 2 and S is of shape [2,n]).
Example:
Suppose we have arrays A3 with dimensions (10,10,10) and S3 with
dimensions (3,2). If we assume
[ 3 6 ]
S3 = reshape( [3, 4, 5, 6, 7, ], [3,2] ) = [ 4 7 ]
[ 5 8 ]
then A3(S3) is a rank-1 extent-2 array that can appear in a variable-
definition context (except as an actual argument associated with a dummy
argument having INTENT(OUT) or INTENT(INOUT)); it specifies the same
array as [ A3(3,4,5), A3(6,7,8) ], which cannot appear in a
variable-definition context. This is different from
A3(S3(1,:),S3(2,:),S3(3,:)), which can appear in a variable-definition
context, but is an object with shape [2,2,2], not [2]. The former is an
arbitrary collection of elements of A3, while the latter is a
rectangular section of A3.
The degenerate case (same as above) is
real :: A(10,10,10)
integer :: S(3) = [ 3, 4, 5 ]
Then A(S) is the same as A(3,4,5), not A(3:3,4:4,5:5). This illustrates
that a rank K+1 subscript with first extent R for a rank R array
produces a rank K result. In this example, K = 0, so A(S) is a scalar,
not a rank-3 array with shape [1,1,1]. I.e., S is NOT a vector
subscript. But many of the same restrictions for vector subscripts
remain, e.g. not being an actual argument corresponding to an
INTENT(OUT) dummy argument.
This can probably be made to work with DIMENSION(..) arrays. A run-time
check is probably required to make sure that in A(S), the first extent
of S is the same as the (dynamic) rank of A. This might be especially
useful to create automatic variables with the same shape as
DIMENSION(..) arrays.