Fortran - 逻辑索引

phd*_*ent 3 indexing fortran logical-operators

假设我有一个矩阵A,其是(m x n)和向量B(m x 1)。这个向量B是一个由零和一组成的向量。

也让标量s是 中元素的总和B

我想创建一个矩阵C,它s x n对应于B等于 1的行,以及一个Dsx 1的向量,这些元素的位置在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]
Run Code Online (Sandbox Code Playgroud)

截至目前,我的 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
Run Code Online (Sandbox Code Playgroud)

在 matlab 中,我可以简单地通过以类似的方式使:C = A(logical(B),:) 和 D 来创建 C。

我怎样才能在 fortran 中做到这一点,避免循环?我已经查看了whereforall语句,但它们并不是我正在寻找的。

Jav*_*tín 5

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

  • 整个数组逻辑索引:掩码必须与数组具有相同的形状,并且返回值的等级为 1(Matlab 中的向量)。这是因为 true 的位置是任意的,因此您不能保证基本上在矩阵中戳孔的结果是矩形的。这就是 PACK 在 Fortran 中所做的

    c = a(a < 1); % Matlab: a(m,n) -> c(s)
    C = PACK(A, A < 1) ! Fortran: A(m,n) -> C(s)
    
    Run Code Online (Sandbox Code Playgroud)
  • 单维逻辑索引:掩码必须是一维的,用于在数组的单维内部进行选择。可以整体选择其他维度或使用它们自己的索引选择其他维度。这就是你想要的

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

但是,PACK 并不直接承认这种用法。@high-performance-mark 的解决方案向您展示了如何执行此壮举:SPREADis basic 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)
Run Code Online (Sandbox Code Playgroud)

由于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)
Run Code Online (Sandbox Code Playgroud)

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

顺便说一句,Fortran确实直接支持整数向量索引,因此您可以使用代码首先生成 D 向量(具有 B 的真实元素的索引),然后执行以下操作:

C = A(D,:) ! Works the same in Fortran!
Run Code Online (Sandbox Code Playgroud)

保存自己的传播和重塑。