首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Fortran中的稀疏数组

Fortran中的稀疏数组
EN

Stack Overflow用户
提问于 2020-06-08 13:09:25
回答 1查看 353关注 0票数 1

我刚接触Fortran (一个月左右)。在此之前,我主要是用Python编写脚本。我目前正在编写一段代码,其中我需要一些方法来存储稀疏数组。数组是正方形的,大约是3E6乘以3E6。起初,我试图用8位实数来存储完整的数组,但遇到了“虚拟内存不足”的问题。

为了减少内存消耗,我编写了一组非常简单的例程和一个类型,将矩阵存储为行、列和值三元组。通过使用另一个数组来复制,然后解除分配和分配,我可以存储这种三元组格式的值。这段代码变得非常慢。当然,由于我在稀疏数组中存储了多少值,它的速度变慢了。

我注意到在Fortran2003和更高版本中有一种非常简单的重新分配方法,使用vec = [vec(:), val]来追加一个元素。这需要使用-assume realloc_lhs进行编译。不幸的是,这和我之前写的版本一样慢。

一个问题是,尽管我知道矩阵会有多大,但我只知道每行(或列)非零元素的数量上限。我没有利用这些信息,这就是为什么我试图重新分配。

我已经附上了使用-assume realloc_lhs的当前版本。我非常感谢任何类型的提示或提示更好的解决方案。

代码语言:javascript
复制
   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
EN

回答 1

Stack Overflow用户

发布于 2020-06-11 17:17:27

有许多数据结构可以很好地处理稀疏数据。我发现C++ std::Vector模型工作得很好。

此模型为超出需要的元素分配空间,并跟踪实际使用的元素数量。大多数情况下,当添加一个元素时,不需要新的内存分配。当需要更多空间时,分配的空间会加倍。这导致O(log(n))分配和更好的性能。

这可以在Fortran中使用矩阵类和矩阵元素类来实现,例如,

代码语言:javascript
复制
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
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62255387

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档