如何实现两个粒子积分<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个,X2,X3)d3X

XX=(X1个,X2,X3)dX

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.