如何实现两个粒子积分<ij | kl>的有效索引功能?


11

这是一个简单的对称枚举问题。我在这里提供了完整的背景知识,但是不需要量子化学知识。

这两种粒子组成是: Ĵ | ķ = &Integral; ψ * Xψ * ĴX 'ψ ķXψ X '一世Ĵ|ķ 它有以下4个对称性: Ĵ | ķ = Ĵ | ķ = ķ | Ĵ = ķ | Ĵ 我已计算在一维数组的积分,并将它们存储的函数,索引如下:

一世Ĵ|ķ=ψ一世XψĴXψķXψX|X-X|d3Xd3X
一世Ĵ|ķ=Ĵ一世|ķ=ķ|一世Ĵ=ķ|Ĵ一世
int2
int2(ijkl2intindex2(i, j, k, l))

ijkl2intindex2考虑到上述对称性,函数将返回唯一索引。唯一的要求是,如果循环遍历i,j,k,l的所有组合(每个从1到n),它将int2连续填充数组,并且将为与上述相关的所有ijkl组合分配相同的索引4种对称性。

我当前在Fortran中的实现在这里。非常慢。有人知道如何有效地做到这一点吗?(使用任何语言。)

ψ一世X一世ķĴ

一世Ĵ|ķ=Ĵ一世|ķ=ķĴ|一世=一世|ķĴ=
=ķ|一世Ĵ=ķ|Ĵ一世=一世|ķĴ=ķĴ|一世

一世Ĵķ一世Ĵ|ķ=一世ķ|ĴĴķ


d3X

1
d3XdX1个dX2dX3X=X1个X2X3d3X

XX=X1个X2X3dX

d3X

Answers:


5

[编辑:第四次的魅力,最后是明智的]

ññ2ñ2+3ŤŤñ+ŤŤñ-1个Ť一种一种Ť一种=一种一种+1个/2

一世ĴŤ一世d一世ĴŤ一世dķŤ一世d一种b一种b

def ascendings(n):
    idx = 0
    for i in range(1,n+1):
        for j in range(1,i+1):
            for k in range(1,i):
                for l in range(1,k+1):
                    idx = idx + 1
                    print(i,j,k,l)
            k=i
            for l in range(1,j+1):
                idx = idx + 1
                print(i,j,k,l)
    return idx

ķ

ŤŤñ-1个

def mixcendings(n):
    idx = 0
    for j in range(2,n+1):
        for i in range(1,j):
            for k in range(1,j):
                for l in range(1,k):
                    print(i,j,k,l)
                    idx = idx + 1
            k=j
            for l in range(1,i+1):
                print(i,j,k,l)
                idx = idx + 1
    return idx

两者的结合提供了完整的集合,因此将两个循环放在一起便提供了完整的索引集合。

ñ

在python中,我们可以编写以下迭代器为每个不同的情况提供idx和i,j,k,l值:

def iterate_quad(n):
    idx = 0
    for i in range(1,n+1):
        for j in range(1,i+1):
            for k in range(1,i):
                for l in range(1,k+1):
                    idx = idx + 1
                    yield (idx,i,j,k,l)
                    #print(i,j,k,l)
            k=i
            for l in range(1,j+1):
                idx = idx + 1
                yield (idx,i,j,k,l)

    for i in range(2,n+1):
        for j in range(1,i):
            for k in range(1,i):
                for l in range(1,k):
                    idx = idx + 1
                    yield (idx,i,j,k,l)
            k=i
            for l in range(1,j+1):
                idx = idx + 1
                yield (idx,i,j,k,l)

一世ñ3+Ĵñ2+ķñ+

integer function squareindex(i,j,k,l,n)
    integer,intent(in)::i,j,k,l,n
    squareindex = (((i-1)*n + (j-1))*n + (k-1))*n + l
end function

integer function generate_order_array(n,arr)
    integer,intent(in)::n,arr(*)
    integer::total,idx,i,j,k,l
    total = n**2 * (n**2 + 3)
    reshape(arr,total)
    idx = 0
    do i=1,n
      do j=1,i
        do k=1,i-1
          do l=1,k
            idx = idx+1
            arr(idx) = squareindex(i,j,k,l,n)
          end do
        end do
        k=i
        do l=1,j
          idx = idx+1
          arr(idx) = squareindex(i,j,k,l,n)
        end do
      end do
    end do

    do i=2,n
      do j=1,i-1
        do k=1,i-1
          do l=1,j
            idx = idx+1
            arr(idx) = squareindex(i,j,k,l,n)
          end do
        end do
        k=i
        do l=1,j
          idx = idx+1
          arr(idx) = squareindex(i,j,k,l,n)
        end do
      end do
    end do

    generate_order_array = idx
  end function

然后遍历它:

maxidx = generate_order_array(n,arr)
do idx=1,maxidx
  i = idx/(n**3) + 1
  t_idx = idx - (i-1)*n**3
  j = t_idx/(n**2) + 1
  t_idx = t_idx - (j-1)*n**2
  k = t_idx/n + 1
  t_idx = t_idx - (k-1)*n
  l = t_idx

  ! now have i,j,k,l, so do stuff
  ! ...
end do

嗨,菲尔,非常感谢您的回答!我测试过,有两个问题。例如idx_all(1、2、3、4、4)== idx_all(1、2、4、3、4)=76。但是<12 | 34> / = <12 | 43>。仅当轨道是真实的时才相等。因此,您的解决方案似乎是针对8个对称的情况(有关简化版本,请参见上面的Fortran示例ijkl2intindex())。第二个问题是索引不是连续的,我将结果粘贴到这里:gist.github.com/2703756。这是我上面的ijkl2intindex2()例程的正确结果:gist.github.com/2703767
昂德里杰·塞蒂克

1
@OndřejČertík:您想要一个标志吗?如果您切换了订单,请让idxpair返回一个符号。
死息2012年

OndřejČertík:我现在看到了区别。正如@Deathbreath指出的那样,您可以否定索引,但是对于整个循环而言,它并不是那么干净。我会思考并更新它。
菲尔H

实际上,否定索引将无法完全正常工作,因为idxpair将获取错误的值。
菲尔H

<一世Ĵ|ķ> =-<Ĵ一世|ķ> =-<一世Ĵ|ķ> = <Ĵ一世|ķ>
一世Ĵķ[一世dXp一种一世[R一世ñdËX一世Ĵ一世ñdËXķ]s一世Gñ一世Ĵs一世Gñķ

3

这是一个使用简单的空间填充曲线的想法,该曲线经修改可为对称情况返回相同的键(所有代码段均在python中)。

# Simple space-filling curve
def forge_key(i, j, k, l, n): 
  return i + j*n + k*n**2 + l*n**3

# Considers the possible symmetries of a key
def forge_key_symmetry(i, j, k, l, n): 
  return min(forge_key(i, j, k, l, n), 
             forge_key(j, i, l, k, n), 
             forge_key(k, l, i, j, n), 
             forge_key(l, k, j, i, n)) 

笔记:

  • 示例是python,但如果将函数内联到您的fortran代码中并展开(i,j,k,l)的内部循环,则应该获得不错的性能。
  • 您可以使用浮点数来计算密钥,然后将其转换为整数以用作索引,这将允许编译器使用浮点单元(例如,可以使用AVX)。
  • 如果N是2的幂,则乘法将只是位移。
  • 对对称的处理在内存中效率不高(即不会产生连续的索引),并且使用的索引总数约为索引数组条目的1/4。

这是n = 2的测试示例:

for i in range(n):
  for j in range(n):
    for k in range(n):
      for l in range(n):
        key = forge_key_symmetry(i, j, k, l, n)
        print i, j, k , l, key

n = 2的输出:

i j k l key
0 0 0 0 0
0 0 0 1 1
0 0 1 0 1
0 0 1 1 3
0 1 0 0 1
0 1 0 1 5
0 1 1 0 6
0 1 1 1 7
1 0 0 0 1
1 0 0 1 6
1 0 1 0 5
1 0 1 1 7
1 1 0 0 3
1 1 0 1 7
1 1 1 0 7
1 1 1 1 15

如果有兴趣,forge_key的反函数为:

# Inverse of forge_key
def split_key(key, n): 
  d = key / n**3
  c = (key - d*n**3) / n**2
  b = (key - c*n**2 - d*n**3) / n 
  a = (key - b*n - c*n**2 - d*n**3)
  return (a, b, c, d)

您是说“如果n是2的幂”,而不是2的倍数?
阿隆·艾玛迪亚

是的,谢谢阿伦。我在去吃饭之前写了这个答案,而绿巨人在写作。
fcruz 2012年

聪明!但是,最大索引不是n ^ 4(如果从0开始则为n ^ 4-1)吗?问题是,对于我希望能够执行的基本大小,它不适合内存。在具有连续索引的情况下,数组的大小为n ^ 2 *(n ^ 2 + 3)/4。Hm,无论如何仅是整个大小的1/4。因此,也许我不必担心内存消耗为4的因素。尽管如此,仍然必须有某种方法仅使用这4种对称性来编码正确的连续索引(比我在帖子中需要做双循环的丑陋解决方案要好)。
昂德里杰·塞蒂克

是的,没错!我不知道如何优雅地解决索引(无需排序和重新编号),但是内存使用率的主要术语是O(N ^ 4)。4的系数应该在内存中大N.小的差异
fcruz

0

这不仅是对压缩对称矩阵索引问题的推广吗?解决方案是offset(i,j)= i *(i + 1)/ 2 + j,不是吗?您不能加倍考虑并为双对称4D数组编制索引吗?需要分支的实现似乎是不必要的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.