Fortran语言中的稀疏数组
我对Fortran非常陌生(一个月左右)。在此之前,我主要使用Python编写脚本。我目前正在编写一个代码,其中我需要一些方法来存储稀疏数组。该阵列为正方形,大约为3E6×3E6。起初我试图用8位实数存储整个数组,但遇到“虚拟内存不足” 为了减少内存消耗,我编写了一组非常简单的例程和一个将矩阵存储为行、列和值三元组的类型。通过使用另一个数组进行复制,然后取消分配和分配,我可以将值存储为这种三重格式。代码的速度非常慢。当然,它会随着我在稀疏数组中存储了多少值而变慢 我注意到,在Fortran 2003及以后的版本中,有一种非常简单的方法可以使用Fortran语言中的稀疏数组,fortran,sparse-matrix,Fortran,Sparse Matrix,我对Fortran非常陌生(一个月左右)。在此之前,我主要使用Python编写脚本。我目前正在编写一个代码,其中我需要一些方法来存储稀疏数组。该阵列为正方形,大约为3E6×3E6。起初我试图用8位实数存储整个数组,但遇到“虚拟内存不足” 为了减少内存消耗,我编写了一组非常简单的例程和一个将矩阵存储为行、列和值三元组的类型。通过使用另一个数组进行复制,然后取消分配和分配,我可以将值存储为这种三重格式。代码的速度非常慢。当然,它会随着我在稀疏数组中存储了多少值而变慢 我注意到,在Fortran 20
vec=[vec(:),val]
附加一个元素来重新分配。这需要使用-假设realloc_lhs
进行编译。不幸的是,这和我以前写的版本一样慢
一个问题是,尽管我知道矩阵有多大,但我只知道每行(或每列)非零元素数量的上限。我没有利用这些信息,这就是我试图重新分配的原因
我已附上使用-假设realloc_lhs
的当前版本。我很感激任何关于更好解决方案的提示或暗示
TYPE sprs
INTEGER :: n, len
REAL, ALLOCATABLE :: val(:)
INTEGER, ALLOCATABLE :: irow(:)
INTEGER, ALLOCATABLE :: icol(:)
END TYPE sprs
!===================================================================================================
SUBROUTINE store_sprs(val,irow,icol,matrix)
!===================================================================================================
!
IMPLICIT NONE
!
! Arguments:
!-----------
REAL, INTENT(IN) :: val
INTEGER, INTENT(IN) :: irow
INTEGER, INTENT(IN) :: icol
TYPE(sprs), INTENT(INOUT) :: matrix
!
! Locals:
!--------
IF (ABS(val)>=1.0E-50) THEN
CALL add2list_re(val, matrix.val)
CALL add2list_int(irow, matrix.irow)
CALL add2list_int(icol, matrix.icol)
ENDIF
END SUBROUTINE store_sprs
SUBROUTINE add2list_re(val,vec)
!===================================================================================================
IMPLICIT NONE
!
! Arguments:
!-----------
REAL, INTENT(IN) :: val
REAL, ALLOCATABLE, INTENT(INOUT) :: vec(:)
!
! Locals:
!--------
vec = [vec(:), val]
END SUBROUTINE add2list_re
!===================================================================================================
SUBROUTINE add2list_int(val,vec)
!===================================================================================================
IMPLICIT NONE
!
! Arguments:
!-----------
INTEGER, INTENT(IN) :: val
INTEGER, ALLOCATABLE, INTENT(INOUT) :: vec(:)
!
! Locals:
!--------
vec = [vec(:), val]
END SUBROUTINE add2list_int
有许多数据结构可以很好地处理稀疏数据。我发现这个模型工作得很好 该模型为超过需要的元素分配空间,并跟踪实际使用的元素数量。大多数情况下,添加元素时,不需要新的内存分配。当需要更多空间时,分配的空间将加倍。这将导致O(log(n))分配和更好的性能 这可以在Fortran中使用矩阵类和矩阵元素类实现,例如
module sparse_matrix
implicit none
private
public :: dp
public :: SparseElement
public :: Sparse
integer, parameter :: dp=selected_real_kind(15,300)
type SparseElement
integer :: irow
integer :: icol
real(dp) :: val
end type
type Sparse
! Only the first no_elements elements will be in use.
integer :: no_elements
type(SparseElement), allocatable :: elements_(:)
contains
procedure, public :: elements
procedure, public :: add_element
end type
interface Sparse
module procedure new_Sparse
end interface
contains
! Initialise the Sparse matrix to an empty matrix.
function new_Sparse() result(this)
implicit none
type(Sparse) :: this
this%no_elements = 0
allocate(this%elements_(0))
end function
! Return only the elements which are in use.
function elements(this) result(output)
implicit none
class(Sparse), intent(in) :: this
type(SparseElement), allocatable :: output(:)
output = this%elements_(:this%no_elements)
end function
! Add an element to the array, automatically allocating memory if needed.
subroutine add_element(this,element)
implicit none
class(Sparse), intent(inout) :: this
type(SparseElement), intent(in) :: element
type(SparseElement), allocatable :: temp(:)
this%no_elements = this%no_elements+1
! This is where memory is added.
! If this%elements_ would overflow, its length is doubled.
if (this%no_elements>size(this%elements_)) then
allocate(temp(2*this%no_elements))
temp(:this%no_elements-1) = this%elements_
this%elements_ = temp
endif
this%elements_(this%no_elements) = element
end subroutine
end module
program main
use sparse_matrix
implicit none
type(Sparse) :: matrix
type(SparseElement), allocatable :: elements(:)
integer :: i
! Initialise the matrix.
matrix = Sparse()
! Add some elements to the matrix.
call matrix%add_element(SparseElement(1,1,1.0_dp))
call matrix%add_element(SparseElement(3,5,7.0_dp))
call matrix%add_element(SparseElement(100,200,300.0_dp))
! Retrieve the elements from the matrix.
elements = matrix%elements()
do i=1,size(elements)
write(*,*) elements(i)%irow, elements(i)%icol, elements(i)%val
enddo
end program
有许多数据结构可以很好地处理稀疏数据。我发现这个模型工作得很好 该模型为超过需要的元素分配空间,并跟踪实际使用的元素数量。大多数情况下,添加元素时,不需要新的内存分配。当需要更多空间时,分配的空间将加倍。这将导致O(log(n))分配和更好的性能 这可以在Fortran中使用矩阵类和矩阵元素类实现,例如
module sparse_matrix
implicit none
private
public :: dp
public :: SparseElement
public :: Sparse
integer, parameter :: dp=selected_real_kind(15,300)
type SparseElement
integer :: irow
integer :: icol
real(dp) :: val
end type
type Sparse
! Only the first no_elements elements will be in use.
integer :: no_elements
type(SparseElement), allocatable :: elements_(:)
contains
procedure, public :: elements
procedure, public :: add_element
end type
interface Sparse
module procedure new_Sparse
end interface
contains
! Initialise the Sparse matrix to an empty matrix.
function new_Sparse() result(this)
implicit none
type(Sparse) :: this
this%no_elements = 0
allocate(this%elements_(0))
end function
! Return only the elements which are in use.
function elements(this) result(output)
implicit none
class(Sparse), intent(in) :: this
type(SparseElement), allocatable :: output(:)
output = this%elements_(:this%no_elements)
end function
! Add an element to the array, automatically allocating memory if needed.
subroutine add_element(this,element)
implicit none
class(Sparse), intent(inout) :: this
type(SparseElement), intent(in) :: element
type(SparseElement), allocatable :: temp(:)
this%no_elements = this%no_elements+1
! This is where memory is added.
! If this%elements_ would overflow, its length is doubled.
if (this%no_elements>size(this%elements_)) then
allocate(temp(2*this%no_elements))
temp(:this%no_elements-1) = this%elements_
this%elements_ = temp
endif
this%elements_(this%no_elements) = element
end subroutine
end module
program main
use sparse_matrix
implicit none
type(Sparse) :: matrix
type(SparseElement), allocatable :: elements(:)
integer :: i
! Initialise the matrix.
matrix = Sparse()
! Add some elements to the matrix.
call matrix%add_element(SparseElement(1,1,1.0_dp))
call matrix%add_element(SparseElement(3,5,7.0_dp))
call matrix%add_element(SparseElement(100,200,300.0_dp))
! Retrieve the elements from the matrix.
elements = matrix%elements()
do i=1,size(elements)
write(*,*) elements(i)%irow, elements(i)%icol, elements(i)%val
enddo
end program
您可以从Fortran代码中找到可调用的开放源代码库稀疏数组的主题在数值计算中很受欢迎,我建议您在对已经完成的工作有基本了解之前,不要开始使用自己的解决方案。您可以开始搜索稀疏数组CSR,并查看结果。所提供的代码有语法错误-matrix.val不是标准Fortran,它应该是matrix%val。我假设这是Intel编译器提供的扩展,我建议您使用-stand f08(我希望)如果你坚持写你自己的(稀疏矩阵不是我喜欢的,但我很惊讶它还不存在),试着一次不向列表中添加一个元素,而是(比如)1000。因此,一个缓冲区中最多有100个,当缓冲区已满时,将其添加到列表中,最后调用添加缓冲区中剩余的内容。内存分配可能非常慢,因此这可能会加快代码的速度。请输入矩阵%irow!您可以从Fortran代码中找到可调用的开放源代码库稀疏数组的主题在数值计算中很受欢迎,我建议您在对已经完成的工作有基本了解之前,不要开始使用自己的解决方案。您可以开始搜索稀疏数组CSR,并查看结果。所提供的代码有语法错误-matrix.val不是标准Fortran,它应该是matrix%val。我假设这是Intel编译器提供的扩展,我建议您使用-stand f08(我希望)如果你坚持写你自己的(稀疏矩阵不是我喜欢的,但我很惊讶它还不存在),试着一次不向列表中添加一个元素,而是(比如)1000。因此,一个缓冲区中最多有100个,当缓冲区已满时,将其添加到列表中,最后调用添加缓冲区中剩余的内容。内存分配可能非常慢,因此这可能会加快代码的速度。请输入矩阵%irow!