experchange > fortran

entropy (01-20-20, 04:34 PM)
Considering a DT like this:

```
module foo_m
implicit none

type foo_t
real, allocatable :: matrix(:,:)
contains
procedure, public :: set
end type foo_t

contains

subroutine set(self, vals)
class(foo_t), intent(out) :: self
real, intent(in) :: vals(:,:)

self%matrix = vals

end subroutine set
end module foo_m
```

a scalar instance `foo_sc` can be `set` in this way:
```
call foo_sc%set(inpmat1)
```

Now, I would like to extend its use to an array of `foo_t` objects (say `foo_arr`) in this way:
```
call foo_arr%set_arr([inpmat1, inpmat2, inpmat3, ..., inpmatn])
```

but I cannot find a way to do so.

I have tried:

1) Writing a TBP `set_arr` like this

```
type mat_t
real, allocatable :: val(:,:)
end type mat_t

subroutine set_arr(self, mats)
class(foo_t), intent(inout) :: self(:)
type(mat_t), intent(in) :: mats(:)

integer :: i

do i = 1, size(self)
call self(i)%set( mats(i)%val )
end do

end subroutine set_arr
```
but gfortran complains:
| procedure, public :: set_arr
| 1
Error: Passed-object dummy argument of ‘set_arr’ at (1) must be scalar

2) Make `set` `elemental`
```
type mat_t
real, allocatable :: val(:,:)
end type mat_t

elemental subroutine set(self, mat)
class(foo_t), intent(out) :: self
type(mat_t), intent(in) :: mat

self%matrix = mat%val

end subroutine set
```
but gfortran complains:
| elemental subroutine set(self, mat)
| 1
Error: INTENT(OUT) argument ‘self’ of pure procedure ‘set’ at (1) may not be polymorphic

Any suggestions?

Thanks.
gah4 (01-20-20, 06:37 PM)
On Monday, January 20, 2020 at 6:34:26 AM UTC-8, entropy wrote:
[..]
> call self(i)%set( mats(i)%val )
> end do
> end subroutine set_arr


Should this be:

call self(i)%set( mats(i))

passing the whole DT instead of just the %val part?

I do agree that the message could explain better.
entropy (01-20-20, 07:14 PM)
On Monday, 20 January 2020 16:37:07 UTC, ga...@u.washington.edu wrote:
> On Monday, January 20, 2020 at 6:34:26 AM UTC-8, entropy wrote:
> Should this be:
> call self(i)%set( mats(i))
> passing the whole DT instead of just the %val part?


I don't think so. Subroutine `set` expects a real array of rank 2 and not the whole DT.

> I do agree that the message could explain better.


No criticism to gfortran's error messages is intended: I trust they comply with the limitations of the language. I just can't find a way to implement a `set` method working with arrays of DT `foo_t`. I thought that it is a common design pattern...
gah4 (01-20-20, 11:28 PM)
On Monday, January 20, 2020 at 9:14:46 AM UTC-8, entropy wrote:

(I wrote)

> > Should this be:
> > call self(i)%set( mats(i))
> > passing the whole DT instead of just the %val part?


> I don't think so. Subroutine `set` expects a real array of
> rank 2 and not the whole DT.


OK, what I was thinking at the time was:

do i = 1, size(self)
call set(self(i), mats(i)%val )

The mats() case reminds me that many times it has been mentioned
that you can't have arrays of pointers, but only arrays of structures
containing pointers. In the mats() case, you can't have an array
of arrays, so you have an array of structure containing an array.

For a contrasting view, in Java you can't have multidimensional
arrays, but instead arrays of arrays. If you:

float mat[][] = new float[10][10];

Java creates an array of float[], and then ten arrays
of float to fill it with. You then pretend it is a 2D array.

I your case, mats would be an array of arrays.

float mats[] = new float[3][][];

and then you might:

mats[0] = new float[10][10];
mats[1] = new float[15][15];
mats[2] = new float[20][20];

Pretty often in Fortran where it says scalar, it doesn't mean
array element.

Note also that in Fortran you can have a function returning an
array, but you can't subscript it. It isn't quite the same as
an array.

end do
gah4 (01-21-20, 12:02 AM)
I was thinking about how to do it in Java, since I am much more
used to OO Java than OO Fortran.

I presumed set is supposed to be a constructor, but maybe not.

In Java, constructors have to have the name of the class, and
you call them with new. Then I think it looks like:

class foo {
float matrix[][];

foo(float vals[][]) {
matrix = new float[vals.length][vals[0].length];
for(int i=0; i<vals.length; i++) {
for(int j=0; j<vals[0].length; j++) matrix[i][j]=vals[i][j];
}
}

static void set_arr(foo foos[], float mats[][][]) {
for(int k=0; k<foos.length; k++) {
foos[k] = new foo(mats[k]);
}
}
}

If set isn't supposed to be a constructor, and I don't know how to
make on in Fortran, then it might look like:

class foo {
float matrix[][];

foo(float vals[][]) {
matrix = new float[vals.length][vals[0].length];
for(int i=0; i<vals.length; i++) {
for(int j=0; j<vals[0].length; j++) matrix[i][j]=vals[i][j];
}
}

void set(float vals[][]) {
for(int i=0; i<vals.length; i++) {
for(int j=0; j<vals[0].length; j++) matrix[i][j]=vals[i][j];
}
}

static void set_arr(foo foos[], float mats[][][]) {
for(int k=0; k<foos.length; k++) {
foos[k].set(mats[k]);
}
}
}

You can't call a class method with an uninitialized reference
variable. The first time is usually a constructor, but could also
be a static method.

Static methods are normally called with the class name, but they
can also be called with a variable of the appropriate class, even
if it hasn't been initialized.

As for your actual question, maybe a pointer (scalar) to the
array element would work.

More interesting if you make it class(*), though.
spectrum (01-21-20, 03:54 AM)
Changing intent(out) to intent(inout) in an elemental TBP might work...?
(In the code below, I have changed "matrix" to "vector" also
to make a test simpler.)

# I've never used this kind of elemental routines (for types), so not very sure
if the code is standard-conforming, but at least GCC-8 and 9 seem to be
working fine with no warning message.

!----------------------------------------------------------------------
module test_m
implicit none

type foo_t
real, allocatable :: vector(:)
contains
procedure :: init
procedure :: set
endtype

type vec_t
real, allocatable :: vals(:)
endtype

contains
elemental subroutine init( self )
class(foo_t), intent(inout) :: self

allocate( self % vector( 0 ) )
end

elemental subroutine set( self, vec )
class(foo_t), intent(inout) :: self
type(vec_t), intent(in) :: vec

self % vector = vec % vals
end
end

!----------------------------------------------------------------------
program main
use test_m
implicit none

type(foo_t) :: foos( 3 )
type(vec_t) :: vecs( 3 )

call foos % init() !! allocate all vectors to size 0

call show( "initial state" )

!................................................. ........

vecs( 1 ) % vals = [1, 2, 3]
vecs( 2 ) % vals = [4, 5, 6]
vecs( 3 ) % vals = [7, 8, 9]

call foos % set( vecs )

call show( "setting foos(:)" )

!................................................. ........

vecs( 1 ) % vals = [-1, -2, -3]
vecs( 2 ) % vals = [-4, -5, -6]

call foos(1:2) % set( vecs(1:2) )

call show( "setting foos(1:2)" )

!................................................. ........

call foos(3) % set( vec = vec_t( [70, 80, 90]) )

call show( "setting foos(3)" )

!................................................. ........

contains
subroutine show( msg )
character(*) :: msg
print *
print *, msg
print *, "[1] vector = ", foos(1) % vector
print *, "[2] vector = ", foos(2) % vector
print *, "[3] vector = ", foos(3) % vector
end
end

[ Result ]

$ gfortran -Wall -fcheck=all test.f90 && ./a.out # with GCC-8/9

initial state
[1] vector =
[2] vector =
[3] vector =

setting foos(:)
[1] vector = 1.00000000 2.00000000 3.00000000
[2] vector = 4.00000000 5.00000000 6.00000000
[3] vector = 7.00000000 8.00000000 9.00000000

setting foos(1:2)
[1] vector = -1.00000000 -2.00000000 -3.00000000
[2] vector = -4.00000000 -5.00000000 -6.00000000
[3] vector = 7.00000000 8.00000000 9.00000000

setting foos(3)
[1] vector = -1.00000000 -2.00000000 -3.00000000
[2] vector = -4.00000000 -5.00000000 -6.00000000
[3] vector = 70.0000000 80.0000000 90.0000000
FortranFan (01-21-20, 09:58 PM)
On Monday, January 20, 2020 at 9:34:26 AM UTC-5, entropy wrote:

> ..
> Now, I would like to extend its use to an array of `foo_t` objects (say `foo_arr`) in this way:
> ```
> call foo_arr%set_arr([inpmat1, inpmat2, inpmat3, ..., inpmatn])
> ```
> but I cannot find a way to do so.
> ..


Note a construct such as [inpmat1, inpmat2, inpmat3, ..., inpmatn] can result in an array temporary and impact performance.

If that is not of concern, then why not consider intrinsic assignment facility in the Fortran standard itself?

type :: foo_t
real, allocatable :: val(:,:)
end type
type(foo_t) :: foo_arr(2)
type(foo_t) :: inpmat1, inpmat2
inpmat1%val = reshape( [ 1.0, 2.0, 3.0, 4.0 ], shape=[2,2] )
inpmat2%val = reshape( [ -1.0, -2.0, -3.0, -4.0 ], shape=[2,2] )
foo_arr = [ inpmat1, inpmat2 ]
print *, "foo_arr(1)%val = ", foo_arr(1)%val
print *, "foo_arr(2)%val = ", foo_arr(2)%val
end

Upon execution,
foo_arr(1)%val = 1.00000000 2.00000000 3.00000000 4.00000000
foo_arr(2)%val = -1.00000000 -2.00000000 -3.00000000 -4.00000000

Or consider defined assignment or ELEMENTAL as shown by @spectrum.
gah4 (01-22-20, 12:36 AM)
On Tuesday, January 21, 2020 at 11:58:38 AM UTC-8, FortranFan wrote:

(snip)

> Note a construct such as [inpmat1, inpmat2, inpmat3, ..., inpmatn]
> can result in an array temporary and impact performance.


> If that is not of concern, then why not consider intrinsic
> assignment facility in the Fortran standard itself?


I was assuming that the OP was interested in learning about
and using the OO features of Fortran.

The program given, then, might not be the actual one that is
needed, but for trying out some features.

For that reason, I tried to understand how the same OO operations
would be done in Java, where I understand them better.

But there are some differences between Fortran and Java (and C).

In C and Java, array subscripting and function call are operators,
and can be applied to any expression of the appropriate type.
For example, a C function can return an array (converted to
a C pointer along the way), or function pointer, which can then
be used directly.

In Fortran, such values need to be assigned to variables, and
then used indirectly.

The OO features are not so well documented in the standard, and
not so easy to follow.
spectrum (01-22-20, 11:06 PM)
Just a slight addition... The "impure" keyword also seems to work for the above patten,
as well as inheritance for set() etc. In the modified code below, I've created
foo_t and foo2_t and select its dynamic type via a user input (just for fun, not for some
real scenario...)

!----------------------------------------------------------------------
module test_m
implicit none

type foo_t
real, allocatable :: vector(:)
contains
procedure :: set => foo_set
endtype

type, extends(foo_t) :: foo2_t
contains
procedure :: set => foo2_set
endtype

type vec_t
real, allocatable :: vals(:)
endtype

contains

impure elemental &
subroutine foo_set( self, vec )
class(foo_t), intent(inout) :: self
type(vec_t), intent(in) :: vec

print *, "foo_set() called"
self % vector = vec % vals
end

impure elemental &
subroutine foo2_set( self, vec )
class(foo2_t), intent(inout) :: self
type(vec_t), intent(in) :: vec

print *, "foo2_set() called"
self % vector = vec % vals * 100
end
end

!----------------------------------------------------------------------
program main
use test_m
implicit none

class(foo_t), allocatable :: fs(:)
type(vec_t) :: vecs( 3 )
integer typeid

print *, "input type of foo [1,2]:"
read *, typeid

if (typeid == 1) then
allocate( foo_t :: fs( 3 ) )
else
allocate( foo2_t :: fs( 3 ) )
endif

vecs( 1 ) % vals = [1, 2, 3]
vecs( 2 ) % vals = [4, 5, 6]
vecs( 3 ) % vals = [7, 8, 9]

call fs % set( vecs )

print *, "[1] vector = ", fs(1) % vector
print *, "[2] vector = ", fs(2) % vector
print *, "[3] vector = ", fs(3) % vector
end

Results (with gfortran-9):

$ ./a.out
input type of foo [1,2]:
1 <-- my input
foo_set() called
foo_set() called
foo_set() called
[1] vector = 1.00000000 2.00000000 3.00000000
[2] vector = 4.00000000 5.00000000 6.00000000
[3] vector = 7.00000000 8.00000000 9.00000000

$ ./a.out
input type of foo [1,2]:
2 <-- my next input
foo2_set() called
foo2_set() called
foo2_set() called
[1] vector = 100.000000 200.000000 300.000000
[2] vector = 400.000000 500.000000 600.000000
[3] vector = 700.000000 800.000000 900.000000
spectrum (01-22-20, 11:11 PM)
Hmm, I'm sorry for ambiguous questions... A more correct version is this one:

Some of my questions:
-- Why intent(out) prohibited for the above elemental routine? Is it related to
the possible FINAL procedure for the "self" variable?
-- Can a user-defined assignment routine handle dynamic dispatch also?
(I haven't tried it for extended types up to now.)
JRR (01-23-20, 02:27 PM)
Am 22.01.20 um 22:11 schrieb spectrum:
> Hmm, I'm sorry for ambiguous questions... A more correct version is this one:
> Some of my questions:
> -- Why intent(out) prohibited for the above elemental routine? Is it related to
> the possible FINAL procedure for the "self" variable?


You mean intent(out) instead of intent(inout)? It works for me.
FortranFan (01-23-20, 04:41 PM)
On Wednesday, January 22, 2020 at 4:11:52 PM UTC-5, spectrum wrote:

> Hmm, I'm sorry for ambiguous questions... A more correct version is this one:
> Some of my questions:
> -- Why intent(out) prohibited for the above elemental routine? Is it related to
> the possible FINAL procedure for the "self" variable?


You appear to have answered the question yourself upthread with PURE attribute in Fortran and its intent to avoid side-effects that can be trampled upon if the INTENT(OUT) dummy argument is permitted to be polymorphic. You showed the IMPURE attribute to the ELEMENTAL subprogram can allow you to bypass the constraint.

> -- Can a user-defined assignment routine handle dynamic dispatch also?
> (I haven't tried it for extended types up to now.)


In principle, yes. As to how suitable, convenient, efficient it may be forone's needs is a separate matter.
jfh (01-23-20, 11:16 PM)
On Friday, January 24, 2020 at 3:42:00 AM UTC+13, FortranFan wrote:
> On Wednesday, January 22, 2020 at 4:11:52 PM UTC-5, spectrum wrote:
> You appear to have answered the question yourself upthread with PURE attribute in Fortran and its intent to avoid side-effects that can be trampled upon if the INTENT(OUT) dummy argument is permitted to be polymorphic. Youshowed the IMPURE attribute to the ELEMENTAL subprogram can allow you to bypass the constraint.
> In principle, yes. As to how suitable, convenient, efficient it may be for one's needs is a separate matter.


Whatever the intent of the variable self those subroutines must be impure because F2018 C1597 begins 'A pure subprogram shall not contain a print-stmt'.
entropy (01-24-20, 08:37 PM)
Thanks for your replies and sorry for not responding earlier.

> As for your actual question, maybe a pointer (scalar) to the
> array element would work.


You're maybe right, but I'd rather stay away from pointers as much as possible.

> Changing intent(out) to intent(inout) in an elemental TBP might work...?


Yes! Many thanks spectrum for finding a solution.

> If that is not of concern, then why not consider intrinsic assignment facility in the Fortran standard itself?


Because I want to make the module private with access only to the class andits public methods, following standard data encapsulation principles (sorry for not having included this detail in my simplified example).

>> Some of my questions:
>> -- Why intent(out) prohibited for the above elemental routine? Is it related to
>> the possible FINAL procedure for the "self" variable?


> You appear to have answered the question yourself upthread with PURE attribute in Fortran
> and its intent to avoid side-effects that can be trampled upon if the INTENT(OUT) dummy
> argument is permitted to be polymorphic. You showed the IMPURE attributeto the ELEMENTAL
> subprogram can allow you to bypass the constraint.


Good catch: all valid points that add to spectrum's solution.

Now, after a little bit of experimenting and thinking, I reached a different solution. I realised that I wanted in my case another class `foo_batch_t`wrapping arrays of `foo_t` (with its own methods) handling "batches" of `foo_t`. Something along these lines.

```
module foo_m
implicit none

type foo_t
real, allocatable :: matrix(:,:)
contains
procedure, public :: set
end type foo_t

type foo_batch_t
type(foo_t), allocatable :: foo(:)
contains
procedure, public :: set_batch
end type foo_batch_t

type mat_t
real, allocatable :: vals(:,:)
end type mat_t

contains

subroutine set(self, vals)
class(foo_t), intent(out) :: self
real, intent(in) :: vals(:,:)

self%matrix = vals

end subroutine set

subroutine set_batch(self, vals)
class(foo_batch_t), intent(out) :: self
type(mat_t), intent(in) :: mat(:)

integer :: i

allocate( self%foo( size(mat) ) )
do i = 1, size(mat)
call self%foo(i)%set( mat(i)%vals )
end do

end subroutine set_batch

end module foo_m
```

to be used in the following way

```
program test
use foo_m
....
type(foo_t) :: foo_sc
type(foo_batch_t) :: foo_arr
real, allocatable :: inpmat1(:,:), inpmat2(:,:), inpmat3(:,:)
....
inpmat1 = reshape([1.,2.,3.,4.,5.,6.],[2,3])
inpmat2 = reshape([4.,5.,6.,7.,8.,9.],[2,3])
inpmat3 = reshape([9.,8.,7.,6.,5.,4.],[2,3])
call foo_sc%set(inpmat1)
call foo_arr%set_batch([ mat_t(inpmat1), mat_t(inpmat2), mat_t(inpmat3) ])
....
```

If you are wondering why I'm not simply using the `elemental` solution suggested by spectrum (that in this simplified case appears totally equivalent or better, for some aspects), in my real case, there are other input arguments in `set_batch` that are arrays with different size than `size(mat)` and`size(self%foo)`. Therefore, I need the additional flexibility, that becomes useful also in other methods beyond `set`.
entropy (01-24-20, 08:41 PM)
Now, getting back to FortranFan's legitimate observation:

> Note a construct such as [inpmat1, inpmat2, inpmat3, ..., inpmatn] can result in an array temporary and impact performance.


In my specific case, matrices are rather small (very rarely beyond 20x20), but I can have many of them working in series (theoretically also in parallel, but that's for another step in the implementation) and the computationally-intensive part happens within a `foo%calc` method, that will be invokedby a `foo_batch%calc` with a simple do loop (as before for `set`).

Do you think that I can be affected by a significant performance hit in my case?

Similar Threads