Indexing Fortran-逻辑索引

Indexing Fortran-逻辑索引,indexing,fortran,logical-operators,Indexing,Fortran,Logical Operators,假设我有一个矩阵a,它是(mxn),还有一个向量B,它是(mx1)。这个向量B是一个由0和1组成的向量 另外,让标量s为B中元素的总和 我想创建一个矩阵C,它是sxn,对应于等于1的B行,以及一个向量D,它是sx1,这些元素的位置在a中 Take as an example: A = [1, 2, 3; 4, 5, 6; 7, 8, 9; 10, 11, 12; 13, 14, 15

假设我有一个矩阵
a
,它是
(mxn)
,还有一个向量
B
,它是
(mx1)
。这个向量
B
是一个由0和1组成的向量

另外,让标量
s
B
中元素的总和

我想创建一个矩阵
C
,它是
sxn
,对应于等于1的
B
行,以及一个向量
D
,它是sx1,这些元素的位置在
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中避免循环?我已经研究了所有语句的
where
,但它们并不完全是我想要的

正如@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)
    
    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的大小相同)的数字。这一次,由于源阵列是一维的,因此不需要对掩模进行任何扩展或对结果进行重塑

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


节省扩展和重塑的时间。

对于需要基于某个向量
u
修剪矩阵
K
的问题(例如,在FEA中反转刚度矩阵时),您不能只进行
Ktrim=PACK(K,u/=0)
,因为
K
是二维的。我发现这样的东西对于移除向量等于零的矩阵元素很有用:

n_trim = COUNT(u/=0)
NonZero(1:n_trim) = PACK([1:n], u/=0)
Ktrim = K(NonZero(1:n_trim),NonZero(1:n_trim))
其中U是一个向量,其零值应从矩阵K中移除,结果存储到Ktrim。K是尺寸n*n,U是尺寸n*1,Ktrim是尺寸n_trim*n_trim

在使用一些“反转”功能进行反转后,可以像这样“取消微调”:

invK = 0
invK(NonZero,NonZero) = invKtrim(1:n_trim,1:n_trim)
PACK()
在搜索方面可能是您的朋友。例如在。更好的可能是可用的,我不建议重复。是“
[1:n]
”意味着数组构造函数吗?当
K
是2D时,为什么不能使用
PACK(K,…)
invK = 0
invK(NonZero,NonZero) = invKtrim(1:n_trim,1:n_trim)