假设大小数组的重新解释

时间:2014-05-02 09:22:52

标签: arrays multidimensional-array fortran

我目前正在尝试在Fortran90(以及Fortran03)中编写的代码中使用PRIMME library。 PRIMME本身是用C语言编写的,但是它附带了一个Fortran77接口,应该可以使用Fortran相对简单。我遇到了一个问题,虽然这与把不同的Fortran风格结合在一起,我还没有找到令人信服的答案:

PRIMME是一个迭代的本征解算器库,可以计算巨大的,对称/埃尔米特矩阵 A 的特征向量和特征值,而无需将矩阵本身存储在内存中。矩阵 A 仅用于在PRIMME库外部实现的矩阵 - 矩阵乘法。然后将一个指向矩阵 - 矩阵乘法程序的函数指针传递到PRIMME库中,然后在内部调用它多次以计算特征向量和特征值。

查看库附带的Fortran77绑定示例,矩阵 - 矩阵乘法例程应具有以下签名。

subroutine MatMatMul(B,C,k,primme)

    real*8 B(*), C(*)
    integer k, primme

    ! calculate C = A * B
end

此处k是矩阵 B C 的列数。 primme作为Fortran整数传递,但它实际上是一个指向包含PRIMME库配置的数据结构的C指针,最值得注意的是特征值问题的维度,即的行数B C 。因此,在MatMatMul方法中,我拥有所需的关于所涉及矩阵大小的所有信息。

不幸的是,矩阵 B C 作为一维假定大小的数组MatMatMulB(*)传递到C(*)。虽然我当然可以自己完成所有索引算法,但我更愿意将BC作为二维数组访问。也可以在它们上使用二维切片。 Fortran代码库的其余部分是使用模块/显式接口和假定的形状数组编写的,因此这也与其余代码更加一致。

有没有办法将 B C 视为具有运行时确定大小的二维Fortan数组?

鉴于我知道运行时的行数和列数,我认为这应该不是一个大问题。我搜索了互联网,但没有找到解决方案(或实际上对问题的任何讨论)。我已经读了很多次,你可以用这些假设的大小数组做的非常有限,因为编译器没有足够的信息来做它们,例如切片。我真的不明白这一点,因为我作为假定的形状数组传递的所有其他可分配数组的形状是在运行时确定的,所以编译器无论如何都没有关于它们的信息但仍然允许我和他们一起做切片。鉴于这一事实,将B(*)视为B(n,k)绝对没有问题。

我还与那些对Fortran有更多经验的同事进行了交谈,虽然他们提出了一些解决方法,例如使MatMatMul成为另一个方法的包装器,该方法接受BC作为显式形状二维数组并将新方法放在模块之外(以便接口不被编译器),他们没有我希望的简单解决方案。

我对Fortran没有太多经验,所以我想我应该在这里问一下,确保我没有错过任何东西。

编辑:根据下面接受的答案,我选择了以下解决方案:

subroutine WrapperMatMatMul(B,C,k,primme)

    real*8 B(*), C(*)
    integer k, primme

    call primme_get_member_f77(primme, PRIMMEF77_n, n)
    call MatMatMul(B,C,k,n,primme)
end

subroutine MatMatMul(B,C,k,n,primme)

    real*8 B(k,n), C(k,n)
    integer k, n, primme

    ! calculate C = A * B
end

然后将指向包装器的指针传递给PRIMME。它不是很漂亮,但效果很好。

1 个答案:

答案 0 :(得分:2)

由于sequence association的规则,允许传递二维数组。在内存存储顺序(专业列)的子例程中,数组被重新解释为维度。

请参阅Fortran标准,或更多非正式介绍http://michaelgoerz.net/blog/2011/05/advanced-array-passing-in-fortran/

唯一存在问题的地方是生成通用接口,在通用消歧中严格执行排名。

subroutine MatMatMul(B,C,k,primme)

    real*8 B(*), C(*)
    integer k, primme

    ! calculate C = A * B
end

real*8 A(10,20), B(20,10)

call MatMatMul(A,B,20,0)

end

甚至

real*8 A(10,20), B(20,10)

call MatMatMul(A,B,20,0)

contains

subroutine MatMatMul(B,C,k,primme)

    real*8 B(*), C(*)
    integer k, primme

    ! calculate C = A * B
end

end

但我更愿意使用Fortran 90内在函数MATMUL。或BLAS子例程dgemv,如果您需要最佳性能,但某些编译器会自动从matmul调用它。