Fortran - 逻辑索引

时间:2016-05-11 21:56:45

标签: indexing fortran logical-operators

假设我有一个A的矩阵(m x n)和一个B的矢量(m x 1)。此向量B是零和1的向量。

同样让标量sB中元素的总和。

我想创建一个矩阵C,其s x n对应于等于1的B行,以及一个sx 1的向量D,其中这些元素在A中的位置。

Take as an example: 

      A = [1, 2, 3; 
           4, 5, 6; 
           7, 8, 9; 
           10, 11, 12; 
           13, 14, 15 ]

        B = [0; 1; 0; 1; 1]

    Then:
C = [ 4, 5, 6; 
     10, 11, 12; 
     13, 14, 15 ]
and
D = [2; 
     4
     5]

截至目前,我的fortran代码如下:

PROGRAM test1
IMPLICIT NONE

 REAL, DIMENSION(5,3) :: A
INTEGER, DIMENSION(5,1) :: B = 0
INTEGER :: i, j, k

k = 1

!Create A matrix
do i=1,5
    do j=1,3
        A(i,j) = k
        k = k+1
    end do
end do

!Create B matrix
B(2,1) = 1
B(4,1) = 1
B(5,1) = 1


end program

在matlab中,我可以简单地创建C:C = A(逻辑(B),:)和D以类似的方式。

我怎么能在fortran中做到这一点,避免循环?我查看了whereforall语句,但它们并不是我想要的。

2 个答案:

答案 0 :(得分:4)

正如@francescalus建议的那样,PACK内在函数是Fortran等效的Matlab逻辑切片,但只是部分。有两种类型的Matlab逻辑索引:

  • 整个数组逻辑索引:掩码必须与数组具有相同的形状,返回的值为1级(Matlab中的向量)。这是因为trues的位置是任意的,因此你无法保证基本上将孔戳到矩阵中的结果将是矩形的。 这就是PACK在Fortran中的作用

    c = a(a < 1); % Matlab: a(m,n) -> c(s)
    C = PACK(A, A < 1) ! Fortran: A(m,n) -> C(s)
    
  • 单维逻辑索引:掩码必须为1-D,用于在数组的单个维度内进行选择。其他尺寸可以整体选择,也可以自己编制索引。 这就是你想要的

    b = a(:,1) > 0; % a(m,n) gives logical b(m)
    c = a(b,:); % a(m,n) selected with b(m) -> c(s,n)
    

但是,PACK不直接承认此用法。 @ high-performance-mark的解决方案向您展示了如何执行此专长:SPREAD基本上是repmat,所以他的解决方案在Matlab中看起来像这样:

b = a(:,1) > 0; % a(m,n) gives logical b(m)
bMat = repmat(b, 1, size(a,2)); % n copies of b(m) -> bMat(m,n)
c = reshape(a(bMat), [sum(b), size(a,2)]); % a(m,n) -> c(s,n)

需要进行最终重塑,因为a(bMat)不是矩阵,而是由于使用了整个矩阵选择形式而导致大小为s * n的向量。另一个答案的Fortran代码正是如此:

c = RESHAPE( &
        PACK(a, & ! The matrix we are selecting from
            SPREAD(b==1, ! The == 1 is because you were using an INTEGER array, not LOGICAL
                   dim=2, ncopies=SIZE(a,2)) & ! This is the repmat-like code
        ), & ! The result of this PACK is cVec(s*n)
        [COUNT(b==1),SIZE(a,2)]) ! Reshape into (s,n)

分配给D的代码非常相似,但是我们不是使用A而是从动态生成的数组中选择包含从1到A的第一个维度的长度的数字(这是与B)的大小相同。这次不需要掩码的任何SPREAD或RESHAPE到结果,因为源数组是1-D。

顺便说一句,Fortran 直接支持整数向量索引,因此您可以先使用代码生成D向量(带有B的真元素的索引),然后 < / em> do:

C = A(D,:) ! Works the same in Fortran!

拯救自己的传播和重塑。

答案 1 :(得分:1)

对于需要基于某些向量K修剪矩阵u的问题(例如,在FEA中反转刚度矩阵时),您不能只做Ktrim = PACK(K,u/=0),因为{ {1}}是2D。我发现类似这样的东西对于删除与向量等于零的位置对应的矩阵元素很有用:

K

其中U是一个向量,应从矩阵K中删除零值,并将结果存储到Ktrim。 K为n * n,U为n * 1,Ktrim为n_trim * n_trim。

在使用某些“反转”功能n_trim = COUNT(u/=0) NonZero(1:n_trim) = PACK([1:n], u/=0) Ktrim = K(NonZero(1:n_trim),NonZero(1:n_trim)) 进行反转之后,您可以像这样“取消修剪”:

invKtrim(1:n_trim,1:n_trim) = invert(Ktrim)