Closed ShatrovOA closed 2 years ago
Hi, thanks for your interest, glad you liked psblas and amg4psblas.
Re abstract classes, in principle you are correct. However, there are a number of methods that are naturally split, in the sense that part of the implementation really belongs in the base class, and part in the derived class. This can be easily accomplished by having the derived class method invoke the base class method as in call this%base_type%foo() and then complete the work with the remaining details. Unfortunately, this cannot work if base_type is abstract, which is why I refrained from doing so.
I will revisit the choice again at the next major design iteration for version 4, where I plan to change quite a few things, and I will run through the various pros and cons again. In the meantime I have partially alleviated some of the work by defining "poor man's templates" for writing the codes, so I am gaining there.
If you have other observations and/or ideas, do not hesitate to let me know.
Cheers Salvatore
On Tue, Jun 29, 2021 at 10:27 PM Oleg Shatrov @.***> wrote:
Dear @sfilippone https://github.com/sfilippone !
Just found you psblas3 and amg4psblas. Looks great, but why don't you abstract classes? I believe it would be much easier to define abstract interface and make class that inherits abstract class to provide implementation, instead of writing functions that throw exceptions.. Compiler will error the incomplete derived type.
Best regards, Oleg
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sfilippone/psblas3/issues/16, or unsubscribe https://github.com/notifications/unsubscribe-auth/AD274T3XOBAB5KDMRNCQS2LTVIUBLANCNFSM47Q6TUPQ .
Consider the following example:
module test_abstract
implicit none
private
public :: base, child
type, abstract :: base
contains
procedure, nopass :: semi_implemented
! This will call 'semi_implemented'
procedure(fully_implemented_interface), pass(self), deferred :: fully_implemented
end type base
abstract interface
subroutine fully_implemented_interface(self)
import :: base
class(base), intent(in) :: self
end subroutine fully_implemented_interface
end interface
type, extends(base) :: child
contains
procedure, pass(self) :: fully_implemented => child_fully_implemented
end type child
contains
subroutine semi_implemented()
print*,'This is base class'
end subroutine semi_implemented
subroutine child_fully_implemented(self)
class(child), intent(in) :: self
print*,'This is child class'
call self%semi_implemented()
end subroutine child_fully_implemented
end module test_abstract
program test
use test_abstract
type(child) :: c
call c%semi_implemented()
call c%fully_implemented()
end program test
This compiles and runs fine with gfortran-7. The result is:
This is base class
This is child class
This is base class
Did you mean that this example won't work or I misunderstood something?
I also noticed that every time I will call "psb_krylov" it will allocate and deallocate memory. I believe that it can be very time consuming if you are planning to solve equation with similar matrix structure and different non-zero values couple million times. That is why I suggest that krylov methods should be derived types with at least 3 methods: init - allocate all necessary memory, solve - Solve preconditioned Ax=b, destroy - release all memory.
What I had in mind is this: module test_abstract implicit none private public :: base, child
type, abstract :: base contains ! This will call 'semi_implemented' procedure(fully_implemented_interface), pass(self) :: method => base_method end type base
end interface
type, extends(base) :: child contains procedure, pass(self) :: method=> child_method end type child
contains
subroutine base_method(self)
class(base), intent(in) :: self
! Do the base thing
print*,'This is base class'
end subroutine base_method
subroutine child_method(self)
class(child), intent(in) :: self
print*,'This is child class'
call self%base%method()
print*,'Now do the child thing'
end subroutine child_method
end module test_abstract
and this cannot work as written
On Wed, Jun 30, 2021 at 11:21 AM Oleg Shatrov @.***> wrote:
Consider the following example:
module test_abstractimplicit none private public :: base, child
type, abstract :: base contains procedure, nopass :: semi_implemented ! This will call 'semi_implemented' procedure(fully_implemented_interface), pass(self), deferred :: fully_implemented end type base
abstract interface subroutine fully_implemented_interface(self) import :: base class(base), intent(in) :: self end subroutine fully_implemented_interface end interface
type, extends(base) :: child contains procedure, pass(self) :: fully_implemented => child_fully_implemented end type child
contains
subroutine semi_implemented()
print*,'This is base class'
end subroutine semi_implemented
subroutine child_fully_implemented(self) class(child), intent(in) :: self
print*,'This is child class' call self%semi_implemented()
end subroutine child_fully_implemented end module test_abstract
program test use test_abstract type(child) :: c
call c%semi_implemented() call c%fully_implemented()end program test
This compiles and runs fine with gfortran-7. The result is:
This is base class This is child class This is base class
Did you mean that this example won't work or I misunderstood something?
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sfilippone/psblas3/issues/16#issuecomment-871237784, or unsubscribe https://github.com/notifications/unsubscribe-auth/AD274T2MNBTONFADJ4ZY3Z3TVLOXZANCNFSM47Q6TUPQ .
There is a method in the amg4psblas "prec" object that will allocate/deallocate memory outside the Krylov method. However, on most Linux based systems, allocation and deallocation are very, very cheap. It is a serious issue when using GPUs, ,though, which is why I defined the allocate_wrk method for the preconditioner object.
On Wed, Jun 30, 2021 at 12:38 PM Oleg Shatrov @.***> wrote:
I also noticed that every time I will call "psb_krylov" it will allocate and deallocate memory. I believe that it can be very time consuming if you are planning to solve equation with similar matrix structure and different non-zero values couple million times. That is why I suggest that krylov methods should be derived types with at least 3 methods: init - allocate all necessary memory, solve - Solve preconditioned Ax=b, destroy - release all memory.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sfilippone/psblas3/issues/16#issuecomment-871289938, or unsubscribe https://github.com/notifications/unsubscribe-auth/AD274TZ4TPBYHVFASZ6N563TVLXZBANCNFSM47Q6TUPQ .
This wont work for 2 reasons, even if base class wasn't abstract:
Here is the reproducer:
module test_abstract
implicit none
private
public :: base, child
type :: base
contains
procedure, pass(self) :: method => base_method
end type base
type, extends(base) :: child
contains
procedure, pass(self) :: method => child_method
end type child
contains
subroutine base_method(self)
class(base), intent(in) :: self
print*,'This is base class'
end subroutine base_method
subroutine child_method(self)
class(child), intent(in) :: self
print*,'This is child class'
call self%method()
end subroutine child_method
end module test_abstract
program test
use test_abstract
type(child) :: c
call c%method()
end program test
Sample output:
...
This is child class
This is child class
This is child class
This is child class
This is child class
This is child class
This is child class
This is child class
This is child class
This is child class
[1] 37726 segmentation fault ./a.out
This can easily handled with some naming convention. For example:
module test_abstract
implicit none
private
public :: base, child
type, abstract :: base
contains
procedure, pass(self), non_overridable :: base_method
procedure(method_interface), pass(self), deferred :: method
end type base
abstract interface
subroutine method_interface(self)
import :: base
class(base), intent(in) :: self
end subroutine method_interface
end interface
type, extends(base) :: child
contains
procedure, pass(self) :: method => child_method
end type child
contains
subroutine base_method(self)
class(base), intent(in) :: self
print*,'This is base class'
end subroutine base_method
subroutine child_method(self)
class(child), intent(in) :: self
print*,'This is child class'
call self%base_method()
end subroutine child_method
end module test_abstract
program test
use test_abstract
type(child) :: c
call c%method()
call c%base_method()
end program test
I declared semi-implemented method as "base_method" and added keyword non_overridable
. Real method goes as deferred
and it can safely execute "base_method"
Metcalf, Reid and Cohen, "Modern Fortran explained", page 266: "Additionally an extended type has a parent component; this is a component that has the type and type parameters of the old type and its name is that of the old type ...... The parent component is particularly useful when invoking procedures that operate on the parent type .. "
On Wed, Jun 30, 2021 at 7:42 PM Oleg Shatrov @.***> wrote:
This wont work for 2 reasons, even if base class wasn't abstract:
- You don't have object of "base" class in your child derived type, you inherit from it.
- By declaring "method => child_method" you override "base_method" with "child_method" and whenever you call "method" inside "child" class it will always call "child_method".
Here is the reproducer:
module test_abstractimplicit none private public :: base, child
type :: base contains procedure, pass(self) :: method => base_method end type base
type, extends(base) :: child contains procedure, pass(self) :: method => child_method end type child
contains
subroutine base_method(self) class(base), intent(in) :: self print*,'This is base class' end subroutine base_method
subroutine child_method(self) class(child), intent(in) :: self
print*,'This is child class' call self%method()
end subroutine child_method end module test_abstract
program test use test_abstract type(child) :: c
call c%method()end program test
Sample output:
... This is child class This is child class This is child class This is child class This is child class This is child class This is child class This is child class This is child class This is child class [1] 37726 segmentation fault ./a.out
This can easily handled with some naming convention. For example:
module test_abstractimplicit none private public :: base, child
type, abstract :: base contains procedure, pass(self), non_overridable :: base_method procedure(method_interface), pass(self), deferred :: method end type base
abstract interface subroutine method_interface(self) import :: base class(base), intent(in) :: self end subroutine method_interface end interface
type, extends(base) :: child contains procedure, pass(self) :: method => child_method end type child
contains
subroutine base_method(self) class(base), intent(in) :: self print*,'This is base class' end subroutine base_method
subroutine child_method(self) class(child), intent(in) :: self
print*,'This is child class' call self%base_method()
end subroutine child_method end module test_abstract
program test use test_abstract type(child) :: c
call c%method() call c%base_method()end program test
I declared semi-implemented method as "base_method" and added keyword non_overridable. Real method goes as deferred and it can safely execute "base_method"
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sfilippone/psblas3/issues/16#issuecomment-871603831, or unsubscribe https://github.com/notifications/unsubscribe-auth/AD274T26IQVELGOP5BD3TWTTVNJSBANCNFSM47Q6TUPQ .
Therefore in the overriding method you have a mechanism to invoke the overridden method. I use this in some places in the library (e.g. in the "clone" and transpose methods) S.
On Thu, Jul 1, 2021 at 8:59 AM Salvatore Filippone < @.***> wrote:
Metcalf, Reid and Cohen, "Modern Fortran explained", page 266: "Additionally an extended type has a parent component; this is a component that has the type and type parameters of the old type and its name is that of the old type ...... The parent component is particularly useful when invoking procedures that operate on the parent type .. "
On Wed, Jun 30, 2021 at 7:42 PM Oleg Shatrov @.***> wrote:
This wont work for 2 reasons, even if base class wasn't abstract:
- You don't have object of "base" class in your child derived type, you inherit from it.
- By declaring "method => child_method" you override "base_method" with "child_method" and whenever you call "method" inside "child" class it will always call "child_method".
Here is the reproducer:
module test_abstractimplicit none private public :: base, child
type :: base contains procedure, pass(self) :: method => base_method end type base
type, extends(base) :: child contains procedure, pass(self) :: method => child_method end type child
contains
subroutine base_method(self) class(base), intent(in) :: self print*,'This is base class' end subroutine base_method
subroutine child_method(self) class(child), intent(in) :: self
print*,'This is child class' call self%method()
end subroutine child_method end module test_abstract
program test use test_abstract type(child) :: c
call c%method()end program test
Sample output:
... This is child class This is child class This is child class This is child class This is child class This is child class This is child class This is child class This is child class This is child class [1] 37726 segmentation fault ./a.out
This can easily handled with some naming convention. For example:
module test_abstractimplicit none private public :: base, child
type, abstract :: base contains procedure, pass(self), non_overridable :: base_method procedure(method_interface), pass(self), deferred :: method end type base
abstract interface subroutine method_interface(self) import :: base class(base), intent(in) :: self end subroutine method_interface end interface
type, extends(base) :: child contains procedure, pass(self) :: method => child_method end type child
contains
subroutine base_method(self) class(base), intent(in) :: self print*,'This is base class' end subroutine base_method
subroutine child_method(self) class(child), intent(in) :: self
print*,'This is child class' call self%base_method()
end subroutine child_method end module test_abstract
program test use test_abstract type(child) :: c
call c%method() call c%base_method()end program test
I declared semi-implemented method as "base_method" and added keyword non_overridable. Real method goes as deferred and it can safely execute "base_method"
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sfilippone/psblas3/issues/16#issuecomment-871603831, or unsubscribe https://github.com/notifications/unsubscribe-auth/AD274T26IQVELGOP5BD3TWTTVNJSBANCNFSM47Q6TUPQ .
Book version of 2011 section 14.2 type extension
On Thu, Jul 1, 2021 at 8:59 AM Salvatore Filippone < @.***> wrote:
Metcalf, Reid and Cohen, "Modern Fortran explained", page 266: "Additionally an extended type has a parent component; this is a component that has the type and type parameters of the old type and its name is that of the old type ...... The parent component is particularly useful when invoking procedures that operate on the parent type .. "
On Wed, Jun 30, 2021 at 7:42 PM Oleg Shatrov @.***> wrote:
This wont work for 2 reasons, even if base class wasn't abstract:
- You don't have object of "base" class in your child derived type, you inherit from it.
- By declaring "method => child_method" you override "base_method" with "child_method" and whenever you call "method" inside "child" class it will always call "child_method".
Here is the reproducer:
module test_abstractimplicit none private public :: base, child
type :: base contains procedure, pass(self) :: method => base_method end type base
type, extends(base) :: child contains procedure, pass(self) :: method => child_method end type child
contains
subroutine base_method(self) class(base), intent(in) :: self print*,'This is base class' end subroutine base_method
subroutine child_method(self) class(child), intent(in) :: self
print*,'This is child class' call self%method()
end subroutine child_method end module test_abstract
program test use test_abstract type(child) :: c
call c%method()end program test
Sample output:
... This is child class This is child class This is child class This is child class This is child class This is child class This is child class This is child class This is child class This is child class [1] 37726 segmentation fault ./a.out
This can easily handled with some naming convention. For example:
module test_abstractimplicit none private public :: base, child
type, abstract :: base contains procedure, pass(self), non_overridable :: base_method procedure(method_interface), pass(self), deferred :: method end type base
abstract interface subroutine method_interface(self) import :: base class(base), intent(in) :: self end subroutine method_interface end interface
type, extends(base) :: child contains procedure, pass(self) :: method => child_method end type child
contains
subroutine base_method(self) class(base), intent(in) :: self print*,'This is base class' end subroutine base_method
subroutine child_method(self) class(child), intent(in) :: self
print*,'This is child class' call self%base_method()
end subroutine child_method end module test_abstract
program test use test_abstract type(child) :: c
call c%method() call c%base_method()end program test
I declared semi-implemented method as "base_method" and added keyword non_overridable. Real method goes as deferred and it can safely execute "base_method"
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sfilippone/psblas3/issues/16#issuecomment-871603831, or unsubscribe https://github.com/notifications/unsubscribe-auth/AD274T26IQVELGOP5BD3TWTTVNJSBANCNFSM47Q6TUPQ .
Wow, did not know this was a thing.. Thanks for info =)
Yep, life (and language standards) are full of surprises :-)
On Thu, Jul 1, 2021 at 1:30 PM Oleg Shatrov @.***> wrote:
Wow, did not know this was a thing.. Thanks for info =)
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sfilippone/psblas3/issues/16#issuecomment-872165970, or unsubscribe https://github.com/notifications/unsubscribe-auth/AD274T22JAISEMJJOFYFTKTTVRGVVANCNFSM47Q6TUPQ .
Dear @sfilippone !
Just found you psblas3 and amg4psblas. Looks great, but why don't you abstract classes? I believe it would be much easier to define abstract interface and make class that inherits abstract class to provide implementation, instead of writing functions that throw exceptions.. Compiler will error the incomplete derived type.
Best regards, Oleg