Fortran 重塑 - N 维转置
Fortran reshape - N-dimensional transpose
我正在尝试用 Fortran 编写一些代码,这需要对 n 维数组进行重新排序。我认为 reshape 内在结合 order
参数应该允许这样做,但是我 运行 遇到了困难。
考虑以下最小示例
program test
implicit none
real, dimension(:,:,:,:,:), allocatable :: matA, matB
integer, parameter :: n1=3, n2=5, n3=7, n4=11, n5=13
integer :: i1, i2, i3, i4, i5
allocate(matA(n1,n2,n3,n4,n5)) !Source array
allocate(matB(n3,n2,n4,n1,n5)) !Reshaped array
!Populate matA
do i5=1, n5
do i4=1, n4
do i3=1, n3
do i2=1, n2
do i1=1, n1
matA(i1,i2,i3,i4,i5) = i1+i2*10+i3*100+i4*10000+i5*1000000
enddo
enddo
enddo
enddo
enddo
print*,"Ad1 : ",matA(:,1,1,1,1),shape(matA)
matB = reshape(matA, shape(matB), order = [3,2,4,1,5])
print*,"Bd4 : ",matB(1,1,1,:,1),shape(matB) !Leading dimension of A is the fourth dimension of B
end program test
我希望这会导致
Ad1 : 1010111.00 1010112.00 1010113.00 3 5 7 11 13
Bd4 : 1010111.00 1010112.00 1010113.00 7 5 11 3 13
但我发现:
Ad1 : 1010111.00 1010112.00 1010113.00 3 5 7 11 13
Bd4 : 1010111.00 1010442.00 1020123.00 7 5 11 3 13
我已经用 gfortran
(4.8.3 和 4.9)和 ifort
(11.0)试过了,发现了相同的结果,所以很可能我只是误解了一些关于如何重塑的东西作品。
任何人都可以阐明我哪里出了问题以及我如何才能实现我的目标吗?
当在reshape
中指定order=
时,以置换下标顺序获取的结果元素对应于源数组的元素。这可能还不完全清楚。 Fortran 2008 标准将此声明为(忽略有关 pad=
的部分)
The elements of the result, taken in permuted subscript order ORDER (1), ..., ORDER (n), are those of SOURCE in normal array element order ..
这意味着从你的 order=[3,2,4,1,5]
示例中可以映射到
matA(1,1,1,1,1), matA(2,1,1,1,1), matA(3,1,1,1,1), matA(1,2,1,1,1), ...
共
matB(1,1,1,1,1), matB(1,1,2,1,1), matB(1,1,3,1,1), matB(1,1,4,1,1), ...
偏移量在 matB
的第三个索引中变化最快,对应于在 matA
的第一个索引中变化最快。 matB
中第二快的变化是维度 2,然后是维度 4,依此类推。
因此,matB(1,1,1:3,1,1)
元素对应 matA(:,1,1,1,1)
。
我已经明确说明 matB
切片的范围,因为您对 matB
的形状有疑问:您希望 matB
的形状是order=
说明符给出的排列的倒数。
你可以把你的例子写成
implicit none
integer, parameter :: n1=3, n2=5, n3=7, n4=11, n5=13
integer matA(n1,n2,n3,n4,n5)
integer matB(n4,n2,n1,n3,n5) ! Inverse of permutation (3 2 4 1 5)
integer i1, i2, i3, i4, i5
forall (i1=1:n1, i2=1:n2, i3=1:n3, i4=1:n4, i5=1:n5) &
matA(i1,i2,i3,i4,i5)=i1+i2*10+i3*100+i4*10000+i5*1000000
print*,"Ad1 : ",matA(:,1,1,1,1),shape(matA)
matB = reshape(matA, shape(matB), order = [3,2,4,1,5])
print*,"Bd3 : ",matB(1,1,:,1,1),shape(matB)
end
或者,如果它是你想要的 matB
的形状,那么它就是想要反转的顺序排列:
matB = reshape(matA, shape(matB), order = [4,2,1,3,5])
乍一看,可能很自然地看到与源维度相关的顺序。然而,以下可能会澄清:无论源的形状如何,重塑的结果都是相同的(使用的是自然顺序的数组元素); order=
值的大小等于 shape=
值的大小。对于其中的第一个,如果源是 [1,2,3,4,5,6]
(回想一下我们如何构造 rank-2 数组),那么 order=
永远不会有任何效果(它必须是 [1]
) 如果在源上使用它。
因为我也觉得 order
对于多维数组的行为很不直观,所以我在下面做了一些代码比较以使情况更加清楚(除了已经完成的@francescalus'回答)。首先,在一个简单的例子中,reshape()
有和没有 order
给出以下结果:
mat = reshape( [1,2,3,4,5,6,7,8], [2,4] )
=> [ 1 3 5 7 ;
2 4 6 8 ]
mat = reshape( [1,2,3,4,5,6,7,8], [2,4], order=[2,1] )
=> [ 1 2 3 4 ;
5 6 7 8 ]
此示例表明,如果没有 order
,元素将以通常的列优先方式填充,而使用 order=[2,1]
,第二维运行速度更快,因此元素将按行填充。这里的关键点是 order
指定 LHS 的哪个维度(而不是源数组)运行得更快(如上面的答案中所强调的)。
现在我们将相同的机制应用于更高维的情况。一、无order
的5维数组的reshape()
matB = reshape( matA, [n3,n2,n4,n1,n5] )
对应显式循环
k = 0
do i5 = 1, n5 !! (5)-th dimension of LHS
do i1 = 1, n1 !! (4)
do i4 = 1, n4 !! (3)
do i2 = 1, n2 !! (2)
do i3 = 1, n3 !! (1)-st dimension of LHS
k = k + 1
matB( i3, i2, i4, i1, i5 ) = matA_seq( k )
enddo;enddo;enddo;enddo;enddo
其中 matA_seq
是 matA
的顺序视图
real, pointer :: matA_seq(:)
matA_seq( 1 : n1*n2*n3*n4*n5 ) => matA(:,:,:,:,:)
现在将 order=[3,2,4,1,5]
附加到 reshape()
,
matB = reshape( matA, [n3,n2,n4,n1,n5], order = [3,2,4,1,5] )
然后更改 DO 循环的顺序,使得
k = 0
do i5 = 1, n5 !! (5)-th dim of LHS
do i3 = 1, n3 !! (1)
do i1 = 1, n1 !! (4)
do i2 = 1, n2 !! (2)
do i4 = 1, n4 !! (3)-rd dim of LHS
k = k + 1
matB( i3, i2, i4, i1, i5 ) = matA_seq( k )
enddo;enddo;enddo;enddo;enddo
这意味着 matB
的第 3 个维度(因此 i4
)运行最快(对应于上述答案中的第二行)。但是OP想要的是
k = 0
do i5 = 1, n5 !! (5)-th dim of LHS
do i4 = 1, n4 !! (3)
do i3 = 1, n3 !! (1)
do i2 = 1, n2 !! (2)
do i1 = 1, n1 !! (4)-th dim of LHS
k = k + 1
matB( i3, i2, i4, i1, i5 ) = matA_seq( k )
enddo;enddo;enddo;enddo;enddo
对应
matB = reshape( matA, [n3,n2,n4,n1,n5], order = [4,2,1,3,5] )
即 francescalus 答案的最后一行。
希望这个比较能进一步说明情况...
我正在尝试用 Fortran 编写一些代码,这需要对 n 维数组进行重新排序。我认为 reshape 内在结合 order
参数应该允许这样做,但是我 运行 遇到了困难。
考虑以下最小示例
program test
implicit none
real, dimension(:,:,:,:,:), allocatable :: matA, matB
integer, parameter :: n1=3, n2=5, n3=7, n4=11, n5=13
integer :: i1, i2, i3, i4, i5
allocate(matA(n1,n2,n3,n4,n5)) !Source array
allocate(matB(n3,n2,n4,n1,n5)) !Reshaped array
!Populate matA
do i5=1, n5
do i4=1, n4
do i3=1, n3
do i2=1, n2
do i1=1, n1
matA(i1,i2,i3,i4,i5) = i1+i2*10+i3*100+i4*10000+i5*1000000
enddo
enddo
enddo
enddo
enddo
print*,"Ad1 : ",matA(:,1,1,1,1),shape(matA)
matB = reshape(matA, shape(matB), order = [3,2,4,1,5])
print*,"Bd4 : ",matB(1,1,1,:,1),shape(matB) !Leading dimension of A is the fourth dimension of B
end program test
我希望这会导致
Ad1 : 1010111.00 1010112.00 1010113.00 3 5 7 11 13
Bd4 : 1010111.00 1010112.00 1010113.00 7 5 11 3 13
但我发现:
Ad1 : 1010111.00 1010112.00 1010113.00 3 5 7 11 13
Bd4 : 1010111.00 1010442.00 1020123.00 7 5 11 3 13
我已经用 gfortran
(4.8.3 和 4.9)和 ifort
(11.0)试过了,发现了相同的结果,所以很可能我只是误解了一些关于如何重塑的东西作品。
任何人都可以阐明我哪里出了问题以及我如何才能实现我的目标吗?
当在reshape
中指定order=
时,以置换下标顺序获取的结果元素对应于源数组的元素。这可能还不完全清楚。 Fortran 2008 标准将此声明为(忽略有关 pad=
的部分)
The elements of the result, taken in permuted subscript order ORDER (1), ..., ORDER (n), are those of SOURCE in normal array element order ..
这意味着从你的 order=[3,2,4,1,5]
示例中可以映射到
matA(1,1,1,1,1), matA(2,1,1,1,1), matA(3,1,1,1,1), matA(1,2,1,1,1), ...
共
matB(1,1,1,1,1), matB(1,1,2,1,1), matB(1,1,3,1,1), matB(1,1,4,1,1), ...
偏移量在 matB
的第三个索引中变化最快,对应于在 matA
的第一个索引中变化最快。 matB
中第二快的变化是维度 2,然后是维度 4,依此类推。
因此,matB(1,1,1:3,1,1)
元素对应 matA(:,1,1,1,1)
。
我已经明确说明 matB
切片的范围,因为您对 matB
的形状有疑问:您希望 matB
的形状是order=
说明符给出的排列的倒数。
你可以把你的例子写成
implicit none
integer, parameter :: n1=3, n2=5, n3=7, n4=11, n5=13
integer matA(n1,n2,n3,n4,n5)
integer matB(n4,n2,n1,n3,n5) ! Inverse of permutation (3 2 4 1 5)
integer i1, i2, i3, i4, i5
forall (i1=1:n1, i2=1:n2, i3=1:n3, i4=1:n4, i5=1:n5) &
matA(i1,i2,i3,i4,i5)=i1+i2*10+i3*100+i4*10000+i5*1000000
print*,"Ad1 : ",matA(:,1,1,1,1),shape(matA)
matB = reshape(matA, shape(matB), order = [3,2,4,1,5])
print*,"Bd3 : ",matB(1,1,:,1,1),shape(matB)
end
或者,如果它是你想要的 matB
的形状,那么它就是想要反转的顺序排列:
matB = reshape(matA, shape(matB), order = [4,2,1,3,5])
乍一看,可能很自然地看到与源维度相关的顺序。然而,以下可能会澄清:无论源的形状如何,重塑的结果都是相同的(使用的是自然顺序的数组元素); order=
值的大小等于 shape=
值的大小。对于其中的第一个,如果源是 [1,2,3,4,5,6]
(回想一下我们如何构造 rank-2 数组),那么 order=
永远不会有任何效果(它必须是 [1]
) 如果在源上使用它。
因为我也觉得 order
对于多维数组的行为很不直观,所以我在下面做了一些代码比较以使情况更加清楚(除了已经完成的@francescalus'回答)。首先,在一个简单的例子中,reshape()
有和没有 order
给出以下结果:
mat = reshape( [1,2,3,4,5,6,7,8], [2,4] )
=> [ 1 3 5 7 ;
2 4 6 8 ]
mat = reshape( [1,2,3,4,5,6,7,8], [2,4], order=[2,1] )
=> [ 1 2 3 4 ;
5 6 7 8 ]
此示例表明,如果没有 order
,元素将以通常的列优先方式填充,而使用 order=[2,1]
,第二维运行速度更快,因此元素将按行填充。这里的关键点是 order
指定 LHS 的哪个维度(而不是源数组)运行得更快(如上面的答案中所强调的)。
现在我们将相同的机制应用于更高维的情况。一、无order
reshape()
matB = reshape( matA, [n3,n2,n4,n1,n5] )
对应显式循环
k = 0
do i5 = 1, n5 !! (5)-th dimension of LHS
do i1 = 1, n1 !! (4)
do i4 = 1, n4 !! (3)
do i2 = 1, n2 !! (2)
do i3 = 1, n3 !! (1)-st dimension of LHS
k = k + 1
matB( i3, i2, i4, i1, i5 ) = matA_seq( k )
enddo;enddo;enddo;enddo;enddo
其中 matA_seq
是 matA
real, pointer :: matA_seq(:)
matA_seq( 1 : n1*n2*n3*n4*n5 ) => matA(:,:,:,:,:)
现在将 order=[3,2,4,1,5]
附加到 reshape()
,
matB = reshape( matA, [n3,n2,n4,n1,n5], order = [3,2,4,1,5] )
然后更改 DO 循环的顺序,使得
k = 0
do i5 = 1, n5 !! (5)-th dim of LHS
do i3 = 1, n3 !! (1)
do i1 = 1, n1 !! (4)
do i2 = 1, n2 !! (2)
do i4 = 1, n4 !! (3)-rd dim of LHS
k = k + 1
matB( i3, i2, i4, i1, i5 ) = matA_seq( k )
enddo;enddo;enddo;enddo;enddo
这意味着 matB
的第 3 个维度(因此 i4
)运行最快(对应于上述答案中的第二行)。但是OP想要的是
k = 0
do i5 = 1, n5 !! (5)-th dim of LHS
do i4 = 1, n4 !! (3)
do i3 = 1, n3 !! (1)
do i2 = 1, n2 !! (2)
do i1 = 1, n1 !! (4)-th dim of LHS
k = k + 1
matB( i3, i2, i4, i1, i5 ) = matA_seq( k )
enddo;enddo;enddo;enddo;enddo
对应
matB = reshape( matA, [n3,n2,n4,n1,n5], order = [4,2,1,3,5] )
即 francescalus 答案的最后一行。
希望这个比较能进一步说明情况...